Updated on 2026-08-14

This commit is contained in:
Tangem 2022-11-15 17:26:57 +03:00
parent 37054e453d
commit 56b064b1a0
20 changed files with 609 additions and 5 deletions

View file

@ -109,6 +109,7 @@ dependencies {
implementation(project(":domain"))
implementation(project(":network"))
implementation(project(":common"))
implementation(project(":core:ui"))
/** AndroidX libraries */
implementation(AndroidX.coreKtx)

View file

@ -9,13 +9,13 @@ import com.tangem.wallet.R
object TangemTypography {
val body1 = TextStyle(
fontFamily = FontFamily(Font(R.font.roboto)),
fontFamily = FontFamily(Font(R.font.roboto_regular)),
fontSize = 16.0.sp,
letterSpacing = 0.5.sp,
lineHeight = 24.0.sp,
)
val body2 = TextStyle(
fontFamily = FontFamily(Font(R.font.roboto)),
fontFamily = FontFamily(Font(R.font.roboto_regular)),
fontSize = 14.0.sp,
letterSpacing = 0.25.sp,
lineHeight = 20.0.sp,
@ -27,13 +27,13 @@ object TangemTypography {
lineHeight = 20.0.sp,
)
val caption = TextStyle(
fontFamily = FontFamily(Font(R.font.roboto)),
fontFamily = FontFamily(Font(R.font.roboto_regular)),
fontSize = 12.0.sp,
letterSpacing = 0.4000000059604645.sp,
lineHeight = 16.0.sp,
)
val headline1 = TextStyle(
fontFamily = FontFamily(Font(R.font.roboto)),
fontFamily = FontFamily(Font(R.font.roboto_regular)),
fontSize = 34.0.sp,
letterSpacing = 0.0.sp,
lineHeight = 44.0.sp,

1
core/ui/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

38
core/ui/build.gradle.kts Normal file
View file

@ -0,0 +1,38 @@
plugins {
id("com.android.library")
kotlin("android")
}
android {
defaultConfig {
compileSdk = AppConfig.compileSdkVersion
minSdk = AppConfig.minSdkVersion
targetSdk = AppConfig.targetSdkVersion
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
isCoreLibraryDesugaringEnabled = false
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = Versions.compose
}
}
dependencies {
/** Compose */
implementation(Compose.foundation)
implementation(Compose.material)
implementation(Compose.uiTooling)
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.tangem.core.ui" />

View file

@ -0,0 +1,72 @@
package com.tangem.core.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
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.TangemTheme
@Preview(widthDp = 360, heightDp = 56, showBackground = true)
@Composable
fun PreviewAppBarWithBackButtonInLightTheme() {
TangemTheme(isDarkTheme = false) {
AppBarWithBackButton(text = "Title", onBackClick = {})
}
}
@Preview(widthDp = 360, heightDp = 56, showBackground = true)
@Composable
fun PreviewAppBarWithBackButtonInDarkTheme() {
TangemTheme(isDarkTheme = true) {
AppBarWithBackButton(text = "Title", onBackClick = {})
}
}
/**
* App bar with back button and optional title
*
* @param text optional title
* @param onBackClick the lambda to be invoked when this icon is pressed
*
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=123%3A287&t=WdN5XpixzZLlQAZO-4"
* >Figma component</a>
*/
@Composable
fun AppBarWithBackButton(text: String? = null, onBackClick: () -> Unit) {
Row(
modifier = Modifier
.background(MaterialTheme.colors.primaryVariant)
.padding(all = dimensionResource(R.dimen.spacing16)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing16)),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painter = painterResource(R.drawable.ic_back),
contentDescription = null,
modifier = Modifier
.size(size = dimensionResource(R.dimen.size24))
.clickable { onBackClick() },
tint = MaterialTheme.colors.onPrimary,
)
if (!text.isNullOrBlank()) {
Text(
text = text,
color = MaterialTheme.colors.onPrimary,
maxLines = 1,
style = MaterialTheme.typography.subtitle1,
)
}
}
}

View file

@ -0,0 +1,164 @@
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(isDarkTheme = 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(isDarkTheme = 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(isDarkTheme = 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(isDarkTheme = 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(isDarkTheme = 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(isDarkTheme = 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(isDarkTheme = 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(isDarkTheme = 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 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>
*/
@Composable
fun PrimaryStartIconButton(text: String, @DrawableRes iconResId: Int, enabled: Boolean = true, onClicked: () -> Unit) {
PrimaryButtonRow(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 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>
*/
@Composable
fun PrimaryEndIconButton(text: String, @DrawableRes iconResId: Int, enabled: Boolean = true, onClicked: () -> Unit) {
PrimaryButtonRow(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(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,
)
}

View file

@ -0,0 +1,79 @@
package com.tangem.core.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TextColorType
import com.tangem.core.ui.res.textColor
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
@Composable
fun Preview_SimpleInfoCard_InLightTheme() {
TangemTheme(isDarkTheme = false) {
SmallInfoCard(startText = "Balance", endText = "0.4405434 BTC")
}
}
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
@Composable
fun Preview_SimpleInfoCard_InDarkTheme() {
TangemTheme(isDarkTheme = true) {
SmallInfoCard(startText = "Balance", endText = "0.4405434 BTC")
}
}
/**
* Small card with text information attached to the edges
*
* @param startText text information attached to the left edge
* @param endText text information attached to the right edge
*
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A339&t=WdN5XpixzZLlQAZO-4"
* >Figma component</a>
*/
@Composable
fun SmallInfoCard(startText: String, endText: String) {
Surface(
shape = RoundedCornerShape(dimensionResource(id = R.dimen.radius12)),
color = MaterialTheme.colors.secondary,
elevation = dimensionResource(id = R.dimen.elevation2),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(dimensionResource(R.dimen.size48))
.padding(
horizontal = dimensionResource(id = R.dimen.spacing16),
vertical = dimensionResource(id = R.dimen.spacing12),
),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = startText,
color = MaterialTheme.colors.textColor(type = TextColorType.TERTIARY),
maxLines = 1,
style = MaterialTheme.typography.subtitle2
)
Text(
text = endText,
color = MaterialTheme.colors.textColor(type = TextColorType.PRIMARY1),
maxLines = 1,
style = MaterialTheme.typography.body2
)
}
}
}

View file

@ -0,0 +1,16 @@
package com.tangem.core.ui.res
import androidx.compose.material.Colors
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
enum class ButtonColorType(val lightColor: Color, val darkColor: Color) {
PRIMARY(lightColor = Dark6, darkColor = Light1),
SECONDARY(lightColor = Light2, darkColor = Dark5),
DISABLED(lightColor = Light2, darkColor = Dark6),
POSITIVE(lightColor = Meadow, darkColor = Meadow),
POSITIVE_DISABLED(lightColor = MagicMint, darkColor = DarkGreen)
}
@Composable
fun Colors.buttonColor(type: ButtonColorType): Color = if (isSystemInDarkTheme) type.darkColor else type.lightColor

View file

@ -0,0 +1,45 @@
package com.tangem.core.ui.res
import androidx.compose.ui.graphics.Color
// region Base
val Black = Color(0xFF000000)
val White = Color(0xFFFFFFFF)
// endregion Base
// region Dark
val Dark1 = Color(0xFF919191)
val Dark2 = Color(0xFF656565)
val Dark3 = Color(0xFF494949)
val Dark4 = Color(0xFF3B3B3B)
val Dark5 = Color(0xFF303030)
val Dark6 = Color(0xFF1E1E1E)
// endregion Dark
// region Light
val Light1 = Color(0xFFF5F5F5)
val Light2 = Color(0xFFEBEBEB)
val Light3 = Color(0xFFD3D3D3)
val Light4 = Color(0xFFC9C9C9)
val Light5 = Color(0xFFB0B0B0)
// endregion Light
// region Green
val Meadow = Color(0xFF1ACE80)
val MagicMint = Color(0xFFA3EBCC)
val DarkGreen = Color(0xFF06311F)
// endregion Green
// region Blue
val Cobalt = Color(0xFF0029FF)
val Azure = Color(0xFF007AFF)
// endregion Blue
// region Red
val Amaranth = Color(0xFFDE1010)
val Flamingo = Color(0xFFED7979)
// endregion Red
// region Yellow
val Tangerine = Color(0xFFFFB71B)
// endregion Yellow

View file

@ -0,0 +1,46 @@
package com.tangem.core.ui.res
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
internal val isSystemInDarkTheme: Boolean = false // TODO: use isSystemInDarkTheme() for automatic color detection
@Composable
fun TangemTheme(
isDarkTheme: Boolean = isSystemInDarkTheme,
content: @Composable () -> Unit,
) {
MaterialTheme(
colors = if (isDarkTheme) DarkColors else LightColors,
typography = TangemTypography,
content = content,
)
}
private val LightColors = lightColors(
primary = Light1,
primaryVariant = Light1,
secondary = White,
background = Light1,
surface = White,
error = Amaranth,
onPrimary = Dark6,
onSecondary = Dark6,
onBackground = Dark6,
onSurface = Dark6,
)
private val DarkColors = darkColors(
primary = Black,
primaryVariant = Black,
secondary = Dark6,
background = Black,
surface = Dark6,
error = Amaranth,
onPrimary = White,
onSecondary = White,
onBackground = White,
onSurface = White,
)

View file

@ -0,0 +1,82 @@
package com.tangem.core.ui.res
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.ExperimentalUnitApi
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.sp
import com.tangem.core.ui.R
private val RobotoFamily = FontFamily(
Font(R.font.roboto_regular, FontWeight.Normal),
Font(R.font.roboto_medium, FontWeight.Medium),
)
@OptIn(ExperimentalUnitApi::class)
val TangemTypography = Typography(
defaultFontFamily = RobotoFamily,
h1 = TextStyle(
fontSize = 34.sp,
fontWeight = FontWeight.Normal,
letterSpacing = TextUnit(0f, TextUnitType.Sp),
lineHeight = TextUnit(44f, TextUnitType.Sp),
),
h2 = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Medium,
letterSpacing = TextUnit(0.18f, TextUnitType.Sp),
lineHeight = TextUnit(32f, TextUnitType.Sp),
),
h3 = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
letterSpacing = TextUnit(0.15f, TextUnitType.Sp),
lineHeight = TextUnit(24f, TextUnitType.Sp),
),
subtitle1 = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
letterSpacing = TextUnit(0.15f, TextUnitType.Sp),
lineHeight = TextUnit(24f, TextUnitType.Sp),
),
subtitle2 = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
letterSpacing = TextUnit(0.5f, TextUnitType.Sp),
lineHeight = TextUnit(24f, TextUnitType.Sp),
),
body1 = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
letterSpacing = TextUnit(0.5f, TextUnitType.Sp),
lineHeight = TextUnit(24f, TextUnitType.Sp),
),
body2 = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
letterSpacing = TextUnit(0.25f, TextUnitType.Sp),
lineHeight = TextUnit(20f, TextUnitType.Sp),
),
button = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
letterSpacing = TextUnit(0.1f, TextUnitType.Sp),
lineHeight = TextUnit(16f, TextUnitType.Sp),
),
caption = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Normal,
letterSpacing = TextUnit(0.4f, TextUnitType.Sp),
lineHeight = TextUnit(16f, TextUnitType.Sp),
),
overline = TextStyle(
fontSize = 10.sp,
fontWeight = FontWeight.Medium,
letterSpacing = TextUnit(1.5f, TextUnitType.Sp),
lineHeight = TextUnit(16f, TextUnitType.Sp),
),
)

View file

@ -0,0 +1,20 @@
package com.tangem.core.ui.res
import androidx.compose.material.Colors
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
enum class TextColorType(val lightColor: Color, val darkColor: Color) {
PRIMARY1(lightColor = Dark6, darkColor = White),
PRIMARY2(lightColor = White, darkColor = Dark6),
SECONDARY(lightColor = Dark2, darkColor = Light5),
TERTIARY(lightColor = Dark1, darkColor = Dark1),
DISABLED(lightColor = Light4, darkColor = Dark3),
ACCENT(lightColor = Meadow, darkColor = Meadow),
WARNING(lightColor = Amaranth, darkColor = Amaranth),
ATTENTION(lightColor = Tangerine, darkColor = Tangerine),
CONSTANT_WHITE(lightColor = White, darkColor = White)
}
@Composable
fun Colors.textColor(type: TextColorType): Color = if (isSystemInDarkTheme) type.darkColor else type.lightColor

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- region Elevation -->
<dimen name="elevation1">1dp</dimen>
<dimen name="elevation2">2dp</dimen>
<dimen name="elevation3">3dp</dimen>
<dimen name="elevation4">4dp</dimen>
<dimen name="elevation6">6dp</dimen>
<dimen name="elevation8">8dp</dimen>
<dimen name="elevation12">12dp</dimen>
<dimen name="elevation16">16dp</dimen>
<dimen name="elevation24">24dp</dimen>
<!-- endregion Elevation -->
<!-- region Radius -->
<dimen name="radius12">12dp</dimen>
<!-- endregion Radius -->
<!-- region Size -->
<dimen name="size24">24dp</dimen>
<dimen name="size48">48dp</dimen>
<dimen name="size200">200dp</dimen>
<!-- endregion Size -->
<!-- region Spacing -->
<dimen name="spacing8">8dp</dimen>
<dimen name="spacing12">12dp</dimen>
<dimen name="spacing16">16dp</dimen>
<dimen name="spacing24">24dp</dimen>
<!-- endregion Spacing -->
</resources>

View file

@ -2,8 +2,15 @@ include(":app")
include(":domain")
include(":network")
include(":common")
// region Core modules
include(":core:datasource")
include(":core:utils")
include(":core:ui")
// endregion Core modules
// region Feature modules
include(":features:referral:data")
include(":features:referral:domain")
include(":features:referral:presentation")
include(":features:referral:presentation")
// endregion Feature modules