Updated on 2026-08-14
This commit is contained in:
commit
f529d886b4
35 changed files with 1591 additions and 279 deletions
|
|
@ -1,7 +1,17 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.ripple.LocalRippleTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
|
|
@ -16,6 +26,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.tap.common.compose.extensions.stringResourceDefault
|
||||
import com.tangem.wallet.R
|
||||
|
||||
|
|
@ -137,8 +148,7 @@ fun ToggledRippleTheme(
|
|||
@Preview
|
||||
@Composable
|
||||
fun ButtonTest() {
|
||||
Scaffold(
|
||||
) {
|
||||
Scaffold {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
PreviewItem("Button") {
|
||||
RectangleButton(text = "Some button") {}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.domain.DomainDialog
|
||||
import com.tangem.domain.redux.domainStore
|
||||
import com.tangem.domain.redux.global.DomainGlobalAction
|
||||
|
|
@ -105,7 +106,7 @@ fun <T> SimpleDialog(
|
|||
modifier = Modifier.padding(22.dp)
|
||||
) {
|
||||
DialogTitle(title = title)
|
||||
LazyColumn() {
|
||||
LazyColumn {
|
||||
items(items) { item ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
|
@ -154,4 +155,4 @@ fun ErrorDialog(
|
|||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// ***************************** Horizontal
|
||||
@Composable
|
||||
fun SpacerH(height: Dp, modifier: Modifier = Modifier) {
|
||||
Spacer(modifier = modifier.height(height))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH4(modifier: Modifier = Modifier) {
|
||||
SpacerH(4.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH8(modifier: Modifier = Modifier) {
|
||||
SpacerH(8.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH16(modifier: Modifier = Modifier) {
|
||||
SpacerH(16.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH24(modifier: Modifier = Modifier) {
|
||||
SpacerH(24.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH32(modifier: Modifier = Modifier) {
|
||||
SpacerH(32.dp, modifier)
|
||||
}
|
||||
|
||||
// ***************************** Vertical
|
||||
@Composable
|
||||
fun SpacerW(width: Dp, modifier: Modifier = Modifier) {
|
||||
Spacer(modifier = modifier.width(width))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW4(modifier: Modifier = Modifier) {
|
||||
SpacerW(4.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW8(modifier: Modifier = Modifier) {
|
||||
SpacerW(8.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW16(modifier: Modifier = Modifier) {
|
||||
SpacerW(16.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW24(modifier: Modifier = Modifier) {
|
||||
SpacerW(24.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW32(modifier: Modifier = Modifier) {
|
||||
SpacerW(32.dp, modifier)
|
||||
}
|
||||
|
||||
// ***************************** Size
|
||||
@Composable
|
||||
fun SpacerS(size: Dp, modifier: Modifier = Modifier) {
|
||||
Spacer(modifier = modifier.size(size))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS4(modifier: Modifier = Modifier) {
|
||||
SpacerS(4.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS8(modifier: Modifier = Modifier) {
|
||||
SpacerS(8.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS16(modifier: Modifier = Modifier) {
|
||||
SpacerS(16.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS24(modifier: Modifier = Modifier) {
|
||||
SpacerS(24.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS32(modifier: Modifier = Modifier) {
|
||||
SpacerS(32.dp, modifier)
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.common.module.ModuleMessage
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.tap.domain.moduleMessage.ModuleMessageConverter
|
||||
import com.tangem.wallet.R
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
|
|
@ -29,12 +30,10 @@ import com.tangem.tap.features.wallet.ui.WalletFragment
|
|||
import com.tangem.wallet.R
|
||||
import timber.log.Timber
|
||||
|
||||
private class Navigation
|
||||
|
||||
fun FragmentActivity.openFragment(
|
||||
screen: AppScreen,
|
||||
addToBackstack: Boolean,
|
||||
fgShareTransition: FragmentShareTransition? = null
|
||||
fgShareTransition: FragmentShareTransition? = null,
|
||||
) {
|
||||
val transaction = this.supportFragmentManager.beginTransaction()
|
||||
val fragment = fragmentFactory(screen)
|
||||
|
|
@ -48,9 +47,14 @@ fun FragmentActivity.openFragment(
|
|||
}
|
||||
}
|
||||
}
|
||||
transaction.replace(R.id.fragment_container, fragment, screen.name)
|
||||
if (addToBackstack && screen != AppScreen.Home) transaction.addToBackStack(null)
|
||||
transaction.commitAllowingStateLoss()
|
||||
if (screen.isDialogFragment) {
|
||||
(fragment as DialogFragment).show(transaction, screen.name)
|
||||
if (addToBackstack && screen != AppScreen.Home) transaction.addToBackStack(null)
|
||||
} else {
|
||||
transaction.replace(R.id.fragment_container, fragment, screen.name)
|
||||
if (addToBackstack && screen != AppScreen.Home) transaction.addToBackStack(null)
|
||||
transaction.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
fun FragmentActivity.popBackTo(screen: AppScreen?, inclusive: Boolean = false) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ data class NavigationState(
|
|||
val activity: WeakReference<AppCompatActivity>? = null,
|
||||
) : StateType
|
||||
|
||||
enum class AppScreen {
|
||||
enum class AppScreen(
|
||||
val isDialogFragment: Boolean = false,
|
||||
) {
|
||||
Home,
|
||||
Shop,
|
||||
Disclaimer,
|
||||
|
|
@ -19,5 +21,5 @@ enum class AppScreen {
|
|||
Details, DetailsSecurity, CardSettings, AppSettings, ResetToFactory,
|
||||
AddTokens, AddCustomToken,
|
||||
WalletConnectSessions,
|
||||
QrScan
|
||||
QrScan,
|
||||
}
|
||||
|
|
@ -2,7 +2,11 @@ package com.tangem.tap.features.home.compose.content
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -20,8 +24,8 @@ import androidx.compose.ui.text.withStyle
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.tap.common.compose.SpacerH16
|
||||
import com.tangem.tap.common.compose.SpacerH32
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.SpacerH32
|
||||
import com.tangem.tap.features.home.compose.StoriesBottomImageAnimation
|
||||
import com.tangem.tap.features.home.compose.StoriesTextAnimation
|
||||
import com.tangem.wallet.R
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.tap.common.compose.extensions.dpSize
|
||||
import com.tangem.tap.common.compose.extensions.halfHeight
|
||||
import com.tangem.tap.common.compose.extensions.toPx
|
||||
|
|
@ -64,7 +64,7 @@ fun StoriesCurrenciesContent(
|
|||
targetOffset = animateTo.toPx(),
|
||||
contentDescription = "Currency row",
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
SpacerH12()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,7 +118,7 @@ fun StoriesWeb3Content(
|
|||
|
||||
@Composable
|
||||
private fun LightenBox(content: @Composable () -> Unit) {
|
||||
Box() {
|
||||
Box {
|
||||
content()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
@ -130,10 +130,10 @@ private fun LightenBox(content: @Composable () -> Unit) {
|
|||
Color.White.copy(alpha = 0f),
|
||||
Color.White.copy(alpha = 0.75f),
|
||||
Color.White.copy(alpha = 0.95f),
|
||||
Color.White
|
||||
)
|
||||
)
|
||||
)
|
||||
Color.White,
|
||||
),
|
||||
),
|
||||
),
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.tap.common.compose.SpacerS8
|
||||
import com.tangem.core.ui.components.SpacerS8
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -4,7 +4,12 @@ import androidx.compose.animation.core.Animatable
|
|||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -13,7 +18,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.SpacerW4
|
||||
import com.tangem.core.ui.components.SpacerW4
|
||||
|
||||
@Composable
|
||||
fun StoriesProgressBar(
|
||||
|
|
|
|||
|
|
@ -6,14 +6,25 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.domain.common.form.Field
|
||||
import com.tangem.domain.features.addCustomToken.TokenBlockchainField
|
||||
import com.tangem.domain.features.addCustomToken.TokenDerivationPathField
|
||||
import com.tangem.domain.features.addCustomToken.TokenField
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.*
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenContractAddressChanged
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenDecimalsChanged
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenDerivationPathChanged
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenNameChanged
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenNetworkChanged
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenSymbolChanged
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState
|
||||
import com.tangem.domain.redux.domainStore
|
||||
import com.tangem.tap.common.compose.*
|
||||
import com.tangem.tap.common.compose.BlockchainSpinner
|
||||
import com.tangem.tap.common.compose.ClearButton
|
||||
import com.tangem.tap.common.compose.ClosePopupTrigger
|
||||
import com.tangem.tap.common.compose.OutlinedTextFieldWidget
|
||||
import com.tangem.tap.common.compose.PasteButton
|
||||
import com.tangem.tap.common.compose.TitleSubtitle
|
||||
import com.tangem.tap.common.extensions.getFromClipboard
|
||||
import com.tangem.wallet.R
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
/** AndroidX libraries */
|
||||
implementation(AndroidX.fragmentKtx)
|
||||
|
||||
/** Compose */
|
||||
implementation(Compose.foundation)
|
||||
|
|
@ -38,4 +40,5 @@ dependencies {
|
|||
|
||||
/** Other libraries */
|
||||
implementation(Library.accompanistSystemUiController)
|
||||
implementation(Library.materialComponent)
|
||||
}
|
||||
|
|
@ -1,183 +1,582 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonColors
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.ButtonElevation
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.ButtonColorType
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TextColorType
|
||||
import com.tangem.core.ui.res.buttonColor
|
||||
import com.tangem.core.ui.res.textColor
|
||||
import com.tangem.core.ui.res.TangemTypography
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
/**
|
||||
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=97%3A103&t=TmfD6UBHPg9uYfev-4)
|
||||
* */
|
||||
@Composable
|
||||
fun Preview_PrimaryStartIconButton_Enabled_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
PrimaryStartIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = true, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryStartIconButton_Enabled_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
PrimaryStartIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = true, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryStartIconButton_Disabled_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
PrimaryStartIconButton(
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem,
|
||||
enabled = false,
|
||||
onClicked = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryStartIconButton_Disabled_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
PrimaryStartIconButton(
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem,
|
||||
enabled = false,
|
||||
onClicked = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryEndIconButton_Enabled_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
PrimaryEndIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = true, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryEndIconButton_Enabled_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
PrimaryEndIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = true, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryEndIconButton_Disabled_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
PrimaryEndIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = false, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryEndIconButton_Disabled_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
PrimaryEndIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = false, onClicked = {})
|
||||
}
|
||||
fun TextButton(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
enabled: Boolean = true,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.None,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
colors = TangemButtonsDefaults.textButtonColors,
|
||||
size = TangemButtonSize.Text,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary button with an icon at the beginning of the layout
|
||||
*
|
||||
* @param modifier button modifier
|
||||
* @param text button text
|
||||
* @param iconResId button icon res id
|
||||
* @param enabled controls the enabled state of the button
|
||||
* @param onClicked the lambda to be invoked when this button is pressed
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=233%3A258&t=WdN5XpixzZLlQAZO-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=97%3A62&t=TmfD6UBHPg9uYfev-4)
|
||||
* */
|
||||
@Composable
|
||||
fun PrimaryStartIconButton(
|
||||
fun TextButtonIconLeft(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
@DrawableRes iconResId: Int,
|
||||
icon: Painter,
|
||||
enabled: Boolean = true,
|
||||
onClicked: () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
PrimaryButtonRow(modifier = modifier, enabled = enabled, onClicked = onClicked) {
|
||||
Icon(
|
||||
painter = painterResource(id = iconResId),
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(dimensionResource(R.dimen.spacing8)))
|
||||
Text(text = text)
|
||||
}
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Left(icon),
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
colors = TangemButtonsDefaults.textButtonColors,
|
||||
size = TangemButtonSize.Text,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PrimaryButton(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
showProgress: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.None,
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.primaryButtonColors,
|
||||
enabled = enabled,
|
||||
showProgress = showProgress,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary button with an icon at the end of the layout
|
||||
*
|
||||
* @param modifier button modifier
|
||||
* @param text button text
|
||||
* @param iconResId button icon res id
|
||||
* @param enabled controls the enabled state of the button
|
||||
* @param onClicked the lambda to be invoked when this button is pressed
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=68%3A47&t=WdN5XpixzZLlQAZO-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=68%3A47&t=TmfD6UBHPg9uYfev-4)
|
||||
* */
|
||||
@Composable
|
||||
fun PrimaryEndIconButton(
|
||||
fun PrimaryButtonIconRight(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
@DrawableRes iconResId: Int,
|
||||
icon: Painter,
|
||||
showProgress: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
onClicked: () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
PrimaryButtonRow(modifier = modifier, enabled = enabled, onClicked = onClicked) {
|
||||
Text(text = text)
|
||||
Spacer(modifier = Modifier.width(dimensionResource(R.dimen.spacing8)))
|
||||
Icon(
|
||||
painter = painterResource(id = iconResId),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Right(icon),
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.primaryButtonColors,
|
||||
enabled = enabled,
|
||||
showProgress = showProgress,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=233%3A258&t=TmfD6UBHPg9uYfev-4)
|
||||
* */
|
||||
@Composable
|
||||
fun PrimaryButtonIconLeft(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
icon: Painter,
|
||||
showProgress: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Left(icon),
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.primaryButtonColors,
|
||||
enabled = enabled,
|
||||
showProgress = showProgress,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PrimaryButtonRow(
|
||||
modifier: Modifier,
|
||||
fun SecondaryButton(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
showProgress: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.None,
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
enabled = enabled,
|
||||
showProgress = showProgress,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=99%3A50&t=TmfD6UBHPg9uYfev-4)
|
||||
* */
|
||||
@Composable
|
||||
fun SecondaryButtonIconRight(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
icon: Painter,
|
||||
showProgress: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Right(icon),
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
enabled = enabled,
|
||||
showProgress = showProgress,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=233%3A262&t=TmfD6UBHPg9uYfev-4)
|
||||
* */
|
||||
@Composable
|
||||
fun SecondaryButtonIconLeft(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
icon: Painter,
|
||||
showProgress: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Left(icon),
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
enabled = enabled,
|
||||
showProgress = showProgress,
|
||||
)
|
||||
}
|
||||
|
||||
// region Defaults
|
||||
@Composable
|
||||
private fun TangemButton(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
icon: TangemButtonIcon,
|
||||
onClick: () -> Unit,
|
||||
colors: ButtonColors,
|
||||
showProgress: Boolean,
|
||||
enabled: Boolean,
|
||||
onClicked: () -> Unit,
|
||||
content: @Composable (RowScope.() -> Unit),
|
||||
size: TangemButtonSize = TangemButtonSize.Default,
|
||||
elevation: ButtonElevation = TangemButtonsDefaults.elevation,
|
||||
) {
|
||||
Button(
|
||||
onClick = onClicked,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(dimensionResource(R.dimen.size48)),
|
||||
.width(IntrinsicSize.Min)
|
||||
.height(IntrinsicSize.Min)
|
||||
.heightIn(size.toHeightDp()),
|
||||
onClick = {
|
||||
if (!showProgress) {
|
||||
onClick()
|
||||
}
|
||||
},
|
||||
enabled = enabled,
|
||||
shape = RoundedCornerShape(dimensionResource(id = R.dimen.radius12)),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
backgroundColor = MaterialTheme.colors.buttonColor(type = ButtonColorType.PRIMARY),
|
||||
contentColor = MaterialTheme.colors.textColor(type = TextColorType.PRIMARY2),
|
||||
disabledBackgroundColor = MaterialTheme.colors.buttonColor(type = ButtonColorType.DISABLED),
|
||||
disabledContentColor = MaterialTheme.colors.textColor(type = TextColorType.DISABLED),
|
||||
),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
elevation = elevation,
|
||||
shape = size.toShape(),
|
||||
colors = colors,
|
||||
) {
|
||||
ButtonContent(
|
||||
text = text,
|
||||
buttonIcon = icon,
|
||||
colors = colors,
|
||||
showProgress = showProgress,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ButtonContent(
|
||||
text: String,
|
||||
buttonIcon: TangemButtonIcon,
|
||||
colors: ButtonColors,
|
||||
enabled: Boolean,
|
||||
showProgress: Boolean,
|
||||
) {
|
||||
val icon = @Composable { painter: Painter ->
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
painter = painter,
|
||||
tint = colors.contentColor(enabled = enabled).value,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
if (showProgress) Box(
|
||||
modifier = Modifier.wrapContentSize(),
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(TangemTheme.dimens.size24),
|
||||
color = colors.contentColor(enabled = enabled).value,
|
||||
strokeWidth = TangemTheme.dimens.size4,
|
||||
)
|
||||
} else Row {
|
||||
if (buttonIcon is TangemButtonIcon.Left) {
|
||||
icon(buttonIcon.painter)
|
||||
Spacer(modifier = Modifier.width(TangemTheme.dimens.size8))
|
||||
}
|
||||
Text(
|
||||
text = text,
|
||||
style = TangemTypography.button,
|
||||
color = colors.contentColor(enabled = enabled).value,
|
||||
)
|
||||
if (buttonIcon is TangemButtonIcon.Right) {
|
||||
Spacer(modifier = Modifier.width(TangemTheme.dimens.size8))
|
||||
icon(buttonIcon.painter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface TangemButtonIcon {
|
||||
val painter: Painter?
|
||||
|
||||
@Immutable
|
||||
data class Left(override val painter: Painter) : TangemButtonIcon
|
||||
|
||||
@Immutable
|
||||
data class Right(override val painter: Painter) : TangemButtonIcon
|
||||
|
||||
object None : TangemButtonIcon {
|
||||
override val painter: Painter? = null
|
||||
}
|
||||
}
|
||||
|
||||
enum class TangemButtonSize {
|
||||
Default,
|
||||
Text,
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemButtonSize.toHeightDp(): Dp = when (this) {
|
||||
TangemButtonSize.Default -> TangemTheme.dimens.size48
|
||||
TangemButtonSize.Text -> TangemTheme.dimens.size40
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemButtonSize.toShape(): Shape = when (this) {
|
||||
TangemButtonSize.Default -> TangemTheme.shapes.roundedCornersMedium
|
||||
TangemButtonSize.Text -> TangemTheme.shapes.roundedCornersSmall
|
||||
}
|
||||
|
||||
private object TangemButtonsDefaults {
|
||||
val elevation: ButtonElevation
|
||||
@Composable get() = ButtonDefaults
|
||||
.elevation(
|
||||
defaultElevation = TangemTheme.dimens.elevation0,
|
||||
pressedElevation = TangemTheme.dimens.elevation0,
|
||||
)
|
||||
|
||||
val primaryButtonColors: ButtonColors
|
||||
@Composable get() = TangemButtonColors(
|
||||
backgroundColor = TangemTheme.colors.button.primary,
|
||||
contentColor = TangemTheme.colors.text.primary2,
|
||||
disabledBackgroundColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
|
||||
val secondaryButtonColors: ButtonColors
|
||||
@Composable get() = TangemButtonColors(
|
||||
backgroundColor = TangemTheme.colors.button.secondary,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
disabledBackgroundColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
|
||||
val textButtonColors: ButtonColors
|
||||
@Composable get() = TangemButtonColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
contentColor = TangemTheme.colors.text.secondary,
|
||||
disabledBackgroundColor = Color.Transparent,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private class TangemButtonColors(
|
||||
private val backgroundColor: Color,
|
||||
private val contentColor: Color,
|
||||
private val disabledBackgroundColor: Color,
|
||||
private val disabledContentColor: Color,
|
||||
) : ButtonColors {
|
||||
@Composable
|
||||
override fun backgroundColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(newValue = if (enabled) backgroundColor else disabledBackgroundColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun contentColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(newValue = if (enabled) contentColor else disabledContentColor)
|
||||
}
|
||||
}
|
||||
// endregion Defaults
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun PrimaryButtonSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
showProgress = true,
|
||||
text = "Manage tokens",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButtonIconRight(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
icon = painterResource(id = R.drawable.ic_tangem),
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButtonIconLeft(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
icon = painterResource(id = R.drawable.ic_tangem),
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButtonIconRight(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
icon = painterResource(id = R.drawable.ic_tangem),
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButtonIconLeft(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
icon = painterResource(id = R.drawable.ic_tangem),
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun PrimaryButtonPreview_Light() {
|
||||
TangemTheme {
|
||||
PrimaryButtonSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun PrimaryButtonPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
PrimaryButtonSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecondaryButtonSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
showProgress = true,
|
||||
text = "Manage tokens",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButtonIconRight(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
icon = painterResource(id = R.drawable.ic_tangem),
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButtonIconLeft(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
icon = painterResource(id = R.drawable.ic_tangem),
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButtonIconRight(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
icon = painterResource(id = R.drawable.ic_tangem),
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButtonIconLeft(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
icon = painterResource(id = R.drawable.ic_tangem),
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun SecondaryButtonPreview_Light() {
|
||||
TangemTheme {
|
||||
SecondaryButtonSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun SecondaryButtonPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
SecondaryButtonSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TextButtonSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
TextButton(
|
||||
text = "Enabled",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
TextButtonIconLeft(
|
||||
text = "Enabled",
|
||||
icon = painterResource(id = R.drawable.ic_plus),
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
TextButton(
|
||||
text = "Enabled",
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
TextButtonIconLeft(
|
||||
text = "Enabled",
|
||||
icon = painterResource(id = R.drawable.ic_plus),
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun TextButtonPreview_Light() {
|
||||
TangemTheme {
|
||||
TextButtonSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun TextButtonPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
TextButtonSample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.ButtonColorType
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TextColorType
|
||||
import com.tangem.core.ui.res.buttonColor
|
||||
import com.tangem.core.ui.res.textColor
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryStartIconButton_Enabled_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
PrimaryStartIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = true, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryStartIconButton_Enabled_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
PrimaryStartIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = true, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryStartIconButton_Disabled_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
PrimaryStartIconButton(
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem,
|
||||
enabled = false,
|
||||
onClicked = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryStartIconButton_Disabled_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
PrimaryStartIconButton(
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem,
|
||||
enabled = false,
|
||||
onClicked = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryEndIconButton_Enabled_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
PrimaryEndIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = true, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryEndIconButton_Enabled_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
PrimaryEndIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = true, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryEndIconButton_Disabled_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
PrimaryEndIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = false, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_PrimaryEndIconButton_Disabled_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
PrimaryEndIconButton(text = "Manage tokens", iconResId = R.drawable.ic_tangem, enabled = false, onClicked = {})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary button with an icon at the beginning of the layout
|
||||
*
|
||||
* @param modifier button modifier
|
||||
* @param text button text
|
||||
* @param iconResId button icon res id
|
||||
* @param enabled controls the enabled state of the button
|
||||
* @param onClicked the lambda to be invoked when this button is pressed
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=233%3A258&t=WdN5XpixzZLlQAZO-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Deprecated("Use PrimaryButtonIconRight instead")
|
||||
@Composable
|
||||
fun PrimaryStartIconButton(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
@DrawableRes iconResId: Int,
|
||||
enabled: Boolean = true,
|
||||
onClicked: () -> Unit,
|
||||
) {
|
||||
PrimaryButtonRow(modifier = modifier, enabled = enabled, onClicked = onClicked) {
|
||||
Icon(
|
||||
painter = painterResource(id = iconResId),
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(dimensionResource(R.dimen.spacing8)))
|
||||
Text(text = text)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary button with an icon at the end of the layout
|
||||
*
|
||||
* @param modifier button modifier
|
||||
* @param text button text
|
||||
* @param iconResId button icon res id
|
||||
* @param enabled controls the enabled state of the button
|
||||
* @param onClicked the lambda to be invoked when this button is pressed
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=68%3A47&t=WdN5XpixzZLlQAZO-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Deprecated("Use PrimaryButtonIconLeft instead")
|
||||
@Composable
|
||||
fun PrimaryEndIconButton(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
@DrawableRes iconResId: Int,
|
||||
enabled: Boolean = true,
|
||||
onClicked: () -> Unit,
|
||||
) {
|
||||
PrimaryButtonRow(modifier = modifier, enabled = enabled, onClicked = onClicked) {
|
||||
Text(text = text)
|
||||
Spacer(modifier = Modifier.width(dimensionResource(R.dimen.spacing8)))
|
||||
Icon(
|
||||
painter = painterResource(id = iconResId),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PrimaryButtonRow(
|
||||
modifier: Modifier,
|
||||
enabled: Boolean,
|
||||
onClicked: () -> Unit,
|
||||
content: @Composable (RowScope.() -> Unit),
|
||||
) {
|
||||
Button(
|
||||
onClick = onClicked,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(dimensionResource(R.dimen.size48)),
|
||||
enabled = enabled,
|
||||
shape = RoundedCornerShape(dimensionResource(id = R.dimen.radius12)),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
backgroundColor = MaterialTheme.colors.buttonColor(type = ButtonColorType.PRIMARY),
|
||||
contentColor = MaterialTheme.colors.textColor(type = TextColorType.PRIMARY2),
|
||||
disabledBackgroundColor = MaterialTheme.colors.buttonColor(type = ButtonColorType.DISABLED),
|
||||
disabledContentColor = MaterialTheme.colors.textColor(type = TextColorType.DISABLED),
|
||||
),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,12 +1,17 @@
|
|||
@file:Suppress("unused")
|
||||
|
||||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.annotation.DimenRes
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun VerticalSpacer(@DimenRes spaceResId: Int) {
|
||||
|
|
@ -16,4 +21,105 @@ fun VerticalSpacer(@DimenRes spaceResId: Int) {
|
|||
@Composable
|
||||
fun HorizontalSpacer(@DimenRes spaceResId: Int) {
|
||||
Spacer(modifier = Modifier.width(dimensionResource(id = spaceResId)))
|
||||
}
|
||||
}
|
||||
|
||||
// region Horizontal
|
||||
@Composable
|
||||
fun SpacerH(height: Dp, modifier: Modifier = Modifier) {
|
||||
Spacer(modifier = modifier.height(height))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH4(modifier: Modifier = Modifier) {
|
||||
SpacerH(4.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH8(modifier: Modifier = Modifier) {
|
||||
SpacerH(8.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH12(modifier: Modifier = Modifier) {
|
||||
SpacerH(12.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH16(modifier: Modifier = Modifier) {
|
||||
SpacerH(16.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH24(modifier: Modifier = Modifier) {
|
||||
SpacerH(24.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH32(modifier: Modifier = Modifier) {
|
||||
SpacerH(32.dp, modifier)
|
||||
}
|
||||
// endregion Horizontal
|
||||
|
||||
// region Vertical
|
||||
@Composable
|
||||
fun SpacerW(width: Dp, modifier: Modifier = Modifier) {
|
||||
Spacer(modifier = modifier.width(width))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW4(modifier: Modifier = Modifier) {
|
||||
SpacerW(4.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW8(modifier: Modifier = Modifier) {
|
||||
SpacerW(8.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW16(modifier: Modifier = Modifier) {
|
||||
SpacerW(16.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW24(modifier: Modifier = Modifier) {
|
||||
SpacerW(24.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW32(modifier: Modifier = Modifier) {
|
||||
SpacerW(32.dp, modifier)
|
||||
}
|
||||
// endregion Vertical
|
||||
|
||||
// region Size
|
||||
@Composable
|
||||
fun SpacerS(size: Dp, modifier: Modifier = Modifier) {
|
||||
Spacer(modifier = modifier.size(size))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS4(modifier: Modifier = Modifier) {
|
||||
SpacerS(4.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS8(modifier: Modifier = Modifier) {
|
||||
SpacerS(8.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS16(modifier: Modifier = Modifier) {
|
||||
SpacerS(16.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS24(modifier: Modifier = Modifier) {
|
||||
SpacerS(24.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerS32(modifier: Modifier = Modifier) {
|
||||
SpacerS(32.dp, modifier)
|
||||
}
|
||||
// endregion Size
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import com.google.accompanist.systemuicontroller.SystemUiController
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
|
||||
@Composable
|
||||
fun SystemBarsEffect(
|
||||
block: SystemUiController.() -> Unit,
|
||||
) {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
SideEffect { block(systemUiController) }
|
||||
}
|
||||
|
|
@ -0,0 +1,414 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.InteractionSource
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.OutlinedTextField
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextFieldColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemTypography
|
||||
|
||||
/**
|
||||
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=213%3A218&t=TmfD6UBHPg9uYfev-4)
|
||||
* */
|
||||
@Composable
|
||||
fun OutlineTextField(
|
||||
modifier: Modifier = Modifier,
|
||||
value: TextFieldValue,
|
||||
onValueChange: (TextFieldValue) -> Unit,
|
||||
label: String? = null,
|
||||
placeholder: String? = null,
|
||||
caption: String? = null,
|
||||
enabled: Boolean = true,
|
||||
isError: Boolean = false,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions(),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
) {
|
||||
TangemTextField(
|
||||
modifier = modifier,
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
singleLine = true,
|
||||
label = label,
|
||||
placeholder = placeholder,
|
||||
caption = caption,
|
||||
enabled = enabled,
|
||||
isError = isError,
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
}
|
||||
|
||||
// region Defaults
|
||||
@Composable
|
||||
private fun TangemTextField(
|
||||
modifier: Modifier = Modifier,
|
||||
value: TextFieldValue,
|
||||
singleLine: Boolean,
|
||||
onValueChange: (TextFieldValue) -> Unit,
|
||||
label: String? = null,
|
||||
placeholder: String? = null,
|
||||
caption: String? = null,
|
||||
enabled: Boolean = true,
|
||||
isError: Boolean = false,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions(),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
colors: TangemTextFieldColors = TangemTextFieldsDefault.defaultTextFieldColors,
|
||||
size: TangemTextFieldSize = TangemTextFieldSize.Default,
|
||||
onClear: () -> Unit = { onValueChange(TextFieldValue()) },
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(IntrinsicSize.Min)
|
||||
.heightIn(size.toHeightDp()),
|
||||
value = value,
|
||||
textStyle = TangemTypography.body1,
|
||||
onValueChange = onValueChange,
|
||||
singleLine = singleLine,
|
||||
enabled = enabled,
|
||||
isError = isError,
|
||||
visualTransformation = visualTransformation,
|
||||
keyboardActions = keyboardActions,
|
||||
keyboardOptions = keyboardOptions,
|
||||
interactionSource = interactionSource,
|
||||
shape = size.toShape(),
|
||||
colors = colors,
|
||||
label = {
|
||||
if (!label.isNullOrEmpty()) {
|
||||
Text(
|
||||
text = label,
|
||||
style = TangemTypography.caption,
|
||||
color = colors.labelColor(
|
||||
enabled = enabled,
|
||||
error = isError,
|
||||
interactionSource = interactionSource,
|
||||
).value,
|
||||
)
|
||||
}
|
||||
},
|
||||
placeholder = {
|
||||
if (!placeholder.isNullOrEmpty()) {
|
||||
Text(
|
||||
text = placeholder,
|
||||
style = TangemTypography.body1,
|
||||
color = colors.placeholderColor(enabled = enabled).value,
|
||||
)
|
||||
}
|
||||
},
|
||||
trailingIcon = {
|
||||
val iconRes by rememberUpdatedState(
|
||||
newValue = if (isError) R.drawable.ic_alert else R.drawable.ic_close,
|
||||
)
|
||||
IconButton(
|
||||
modifier = Modifier.size(32.dp),
|
||||
onClick = onClear,
|
||||
enabled = !isError && enabled,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
painter = painterResource(id = iconRes),
|
||||
tint = colors.trailingIconColor(enabled = enabled, isError = isError).value,
|
||||
contentDescription = "Clear input",
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = !caption.isNullOrEmpty(),
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
if (caption.isNullOrEmpty()) return@AnimatedVisibility
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
text = caption,
|
||||
style = TangemTypography.body1,
|
||||
color = colors.captionColor(enabled = enabled, isError = isError).value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum class TangemTextFieldSize {
|
||||
Default
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemTextFieldSize.toHeightDp(): Dp = when (this) {
|
||||
TangemTextFieldSize.Default -> TangemTheme.dimens.size56
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemTextFieldSize.toShape(): Shape = when (this) {
|
||||
TangemTextFieldSize.Default -> TangemTheme.shapes.roundedCornersSmall2
|
||||
}
|
||||
|
||||
private object TangemTextFieldsDefault {
|
||||
val defaultTextFieldColors: TangemTextFieldColors
|
||||
@Composable @Stable get() = TangemTextFieldColors(
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
disabledTextColor = TangemTheme.colors.text.disabled,
|
||||
backgroundColor = Color.Transparent,
|
||||
cursorColor = TangemTheme.colors.icon.primary1,
|
||||
errorCursorColor = TangemTheme.colors.icon.warning,
|
||||
focusedIndicatorColor = TangemTheme.colors.icon.primary1,
|
||||
unfocusedIndicatorColor = TangemTheme.colors.stroke.primary,
|
||||
disabledIndicatorColor = TangemTheme.colors.stroke.primary,
|
||||
errorIndicatorColor = TangemTheme.colors.icon.warning,
|
||||
leadingIconColor = TangemTheme.colors.icon.informative,
|
||||
disabledLeadingIconColor = Color.Transparent,
|
||||
errorLeadingIconColor = TangemTheme.colors.icon.warning,
|
||||
trailingIconColor = TangemTheme.colors.icon.informative,
|
||||
disabledTrailingIconColor = Color.Transparent,
|
||||
errorTrailingIconColor = TangemTheme.colors.icon.warning,
|
||||
focusedLabelColor = TangemTheme.colors.text.primary1,
|
||||
unfocusedLabelColor = TangemTheme.colors.text.secondary,
|
||||
disabledLabelColor = TangemTheme.colors.text.disabled,
|
||||
errorLabelColor = TangemTheme.colors.icon.warning,
|
||||
placeholderColor = TangemTheme.colors.text.secondary,
|
||||
disabledPlaceholderColor = TangemTheme.colors.text.disabled,
|
||||
captionColor = TangemTheme.colors.text.tertiary,
|
||||
disabledCaptionColor = TangemTheme.colors.text.disabled,
|
||||
errorCaptionColor = TangemTheme.colors.icon.warning,
|
||||
)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private data class TangemTextFieldColors(
|
||||
private val textColor: Color,
|
||||
private val disabledTextColor: Color,
|
||||
private val cursorColor: Color,
|
||||
private val errorCursorColor: Color,
|
||||
private val focusedIndicatorColor: Color,
|
||||
private val unfocusedIndicatorColor: Color,
|
||||
private val errorIndicatorColor: Color,
|
||||
private val disabledIndicatorColor: Color,
|
||||
private val leadingIconColor: Color,
|
||||
private val disabledLeadingIconColor: Color,
|
||||
private val errorLeadingIconColor: Color,
|
||||
private val trailingIconColor: Color,
|
||||
private val disabledTrailingIconColor: Color,
|
||||
private val errorTrailingIconColor: Color,
|
||||
private val backgroundColor: Color,
|
||||
private val focusedLabelColor: Color,
|
||||
private val unfocusedLabelColor: Color,
|
||||
private val disabledLabelColor: Color,
|
||||
private val errorLabelColor: Color,
|
||||
private val placeholderColor: Color,
|
||||
private val disabledPlaceholderColor: Color,
|
||||
private val captionColor: Color,
|
||||
private val disabledCaptionColor: Color,
|
||||
private val errorCaptionColor: Color,
|
||||
) : TextFieldColors {
|
||||
|
||||
@Composable
|
||||
override fun leadingIconColor(enabled: Boolean, isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(
|
||||
when {
|
||||
!enabled -> disabledLeadingIconColor
|
||||
isError -> errorLeadingIconColor
|
||||
else -> leadingIconColor
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun trailingIconColor(enabled: Boolean, isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(
|
||||
when {
|
||||
!enabled -> disabledTrailingIconColor
|
||||
isError -> errorTrailingIconColor
|
||||
else -> trailingIconColor
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun indicatorColor(
|
||||
enabled: Boolean,
|
||||
isError: Boolean,
|
||||
interactionSource: InteractionSource,
|
||||
): State<Color> {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
|
||||
val targetValue = when {
|
||||
!enabled -> disabledIndicatorColor
|
||||
isError -> errorIndicatorColor
|
||||
focused -> focusedIndicatorColor
|
||||
else -> unfocusedIndicatorColor
|
||||
}
|
||||
return if (enabled) {
|
||||
animateColorAsState(targetValue, tween(durationMillis = 120))
|
||||
} else {
|
||||
rememberUpdatedState(targetValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun backgroundColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(backgroundColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun placeholderColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (enabled) placeholderColor else disabledPlaceholderColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun labelColor(
|
||||
enabled: Boolean,
|
||||
error: Boolean,
|
||||
interactionSource: InteractionSource,
|
||||
): State<Color> {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
|
||||
val targetValue = when {
|
||||
!enabled -> disabledLabelColor
|
||||
error -> errorLabelColor
|
||||
focused -> focusedLabelColor
|
||||
else -> unfocusedLabelColor
|
||||
}
|
||||
return rememberUpdatedState(targetValue)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun textColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (enabled) textColor else disabledTextColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun cursorColor(isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (isError) errorCursorColor else cursorColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun captionColor(enabled: Boolean, isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(
|
||||
newValue = when {
|
||||
!enabled -> disabledCaptionColor
|
||||
isError -> errorCaptionColor
|
||||
else -> captionColor
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion Defaults
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun OutlineTextFieldSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
val text = TextFieldValue(text = "Input")
|
||||
OutlineTextField(
|
||||
value = text,
|
||||
onValueChange = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
OutlineTextField(
|
||||
value = text,
|
||||
onValueChange = { /* no-op */ },
|
||||
label = "Default",
|
||||
placeholder = "Placeholder",
|
||||
caption = "Supporting text",
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
OutlineTextField(
|
||||
value = text,
|
||||
onValueChange = { /* no-op */ },
|
||||
enabled = false,
|
||||
label = "Disabled",
|
||||
placeholder = "Placeholder",
|
||||
caption = "Supporting text",
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
OutlineTextField(
|
||||
value = text,
|
||||
onValueChange = { /* no-op */ },
|
||||
isError = true,
|
||||
label = "Error",
|
||||
placeholder = "Placeholder",
|
||||
caption = "Supporting text",
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
OutlineTextField(
|
||||
value = TextFieldValue(),
|
||||
onValueChange = { /* no-op */ },
|
||||
label = "Default without value",
|
||||
placeholder = "Placeholder",
|
||||
caption = "Supporting text",
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun OutlineTextFieldPreview_Light() {
|
||||
TangemTheme {
|
||||
OutlineTextFieldSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun OutlineTextFieldPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
OutlineTextFieldSample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -23,6 +23,7 @@ import com.tangem.core.ui.res.iconColor
|
|||
* @see <a href="https://www.figma.com/file/17JyRbuUEZ42DluaFEuGQk/Atoms?node-id=135%3A25&t=3dvxYMZBox4jxJc1-4">
|
||||
* Figma Component</a>
|
||||
*/
|
||||
@Deprecated(message = "Use Hand component instead")
|
||||
@Composable
|
||||
fun BottomSheetIndicator() {
|
||||
Box(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
package com.tangem.core.ui.components.atoms
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* [Show in Figma](https://www.figma.com/file/17JyRbuUEZ42DluaFEuGQk/Atoms?node-id=135%3A25&t=Jo68S6ilyewVU8MV-4)
|
||||
* */
|
||||
@Composable
|
||||
fun Hand(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing8)
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(TangemTheme.dimens.size32)
|
||||
.height(TangemTheme.dimens.size4)
|
||||
.background(
|
||||
color = TangemTheme.colors.icon.inactive,
|
||||
shape = TangemTheme.shapes.roundedCornersSmall,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun HandSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Hand()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun HandPreview_Light() {
|
||||
TangemTheme {
|
||||
HandSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun HandPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
HandSample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package com.tangem.core.ui.fragments
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
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.State
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
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.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
abstract class ComposeBottomSheetFragment<ScreenState> : BottomSheetDialogFragment() {
|
||||
open val initialBottomSheetState = BottomSheetBehavior.STATE_EXPANDED
|
||||
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
open val expandedHeightFraction: Float? = null
|
||||
|
||||
override fun getTheme(): Int = R.style.AppTheme_TransparentBottomSheetDialog
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
return createComposeView(inflater.context)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
|
||||
(dialog as BottomSheetDialog).behavior.apply {
|
||||
state = initialBottomSheetState
|
||||
skipCollapsed = true
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun createComposeView(context: Context): View {
|
||||
return ComposeView(context).apply {
|
||||
setContent {
|
||||
TangemTheme {
|
||||
val backgroundColor = TangemTheme.colors.background.plain
|
||||
SystemBarsEffect {
|
||||
setSystemBarsColor(
|
||||
color = backgroundColor,
|
||||
)
|
||||
}
|
||||
|
||||
ScreenContent(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.let {
|
||||
if (expandedHeightFraction != null) it.fillMaxHeight(expandedHeightFraction!!) else it
|
||||
}
|
||||
.background(
|
||||
color = backgroundColor,
|
||||
shape = TangemTheme.shapes.bottomSheet,
|
||||
),
|
||||
state = provideState().value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
protected abstract fun provideState(): State<ScreenState>
|
||||
|
||||
@Composable
|
||||
protected abstract fun ScreenContent(
|
||||
modifier: Modifier,
|
||||
state: ScreenState,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.tangem.core.ui.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
abstract class ComposeFragment<ScreenState> : Fragment() {
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
return createComposeView(inflater.context)
|
||||
}
|
||||
|
||||
private fun createComposeView(context: Context): View {
|
||||
return ComposeView(context).apply {
|
||||
setContent {
|
||||
TangemTheme {
|
||||
val backgroundColor = TangemTheme.colors.background.primary
|
||||
SystemBarsEffect {
|
||||
setSystemBarsColor(
|
||||
color = backgroundColor,
|
||||
)
|
||||
}
|
||||
|
||||
ScreenContent(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = backgroundColor),
|
||||
state = provideState().value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
protected abstract fun provideState(): State<ScreenState>
|
||||
|
||||
@Composable
|
||||
protected abstract fun ScreenContent(
|
||||
modifier: Modifier,
|
||||
state: ScreenState,
|
||||
)
|
||||
}
|
||||
10
core/ui/src/main/res/drawable/ic_alert.xml
Normal file
10
core/ui/src/main/res/drawable/ic_alert.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M11,15H13V17H11V15ZM11,7H13V13H11V7ZM12,2C6.47,2 2,6.5 2,12C2,14.652 3.054,17.196 4.929,19.071C5.858,20 6.96,20.736 8.173,21.239C9.386,21.741 10.687,22 12,22C14.652,22 17.196,20.946 19.071,19.071C20.946,17.196 22,14.652 22,12C22,10.687 21.741,9.386 21.239,8.173C20.736,6.96 20,5.858 19.071,4.929C18.142,4 17.04,3.264 15.827,2.761C14.614,2.259 13.313,2 12,2ZM12,20C9.878,20 7.843,19.157 6.343,17.657C4.843,16.157 4,14.122 4,12C4,9.878 4.843,7.843 6.343,6.343C7.843,4.843 9.878,4 12,4C14.122,4 16.157,4.843 17.657,6.343C19.157,7.843 20,9.878 20,12C20,14.122 19.157,16.157 17.657,17.657C16.157,19.157 14.122,20 12,20Z" />
|
||||
</vector>
|
||||
10
core/ui/src/main/res/drawable/ic_close.xml
Normal file
10
core/ui/src/main/res/drawable/ic_close.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
|
||||
</vector>
|
||||
10
core/ui/src/main/res/drawable/ic_plus.xml
Normal file
10
core/ui/src/main/res/drawable/ic_plus.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
||||
</vector>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<string name="shop_2_cards">2 карты</string>
|
||||
<string name="shop_shipping">Доставка</string>
|
||||
<string name="shop_free">Бесплатно</string>
|
||||
<string name="shop_i_have_a_promo_code">У меня есть промо-код...</string>
|
||||
<string name="shop_i_have_a_promo_code">У меня есть промо-код…</string>
|
||||
<string name="shop_total">Итого</string>
|
||||
<string name="shop_other_payment_methods">Другие способы оплаты</string>
|
||||
<string name="shop_buy_now">Купить сейчас</string>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<string name="shop_2_cards">2 cards</string>
|
||||
<string name="shop_shipping">Shipping</string>
|
||||
<string name="shop_free">Free</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code...</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code…</string>
|
||||
<string name="shop_total">Total</string>
|
||||
<string name="shop_other_payment_methods">Other payment methods</string>
|
||||
<string name="shop_buy_now">Buy now</string>
|
||||
12
core/ui/src/main/res/values/styles.xml
Normal file
12
core/ui/src/main/res/values/styles.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="AppTheme.TransparentBottomSheetDialog" parent="ThemeOverlay.MaterialComponents.BottomSheetDialog">
|
||||
<item name="bottomSheetStyle">@style/TransparentBottomSheetDialog</item>
|
||||
<item name="backgroundTint">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="TransparentBottomSheetDialog" parent="Widget.MaterialComponents.BottomSheet">
|
||||
<item name="backgroundTint">@android:color/transparent</item>
|
||||
</style>
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue