Updated on 2026-08-14
This commit is contained in:
commit
bc84d8f5f8
579 changed files with 13604 additions and 3422 deletions
|
|
@ -9,9 +9,6 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
/** Project - Common */
|
||||
implementation(projects.common)
|
||||
|
||||
/** Project - Domain */
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.appTheme.models)
|
||||
|
|
@ -25,6 +22,12 @@ dependencies {
|
|||
implementation(deps.androidx.fragment.ktx)
|
||||
implementation(deps.androidx.paging.runtime)
|
||||
implementation(deps.androidx.palette)
|
||||
implementation(deps.androidx.windowManager) {
|
||||
exclude(
|
||||
deps.kotlin.coroutines.android.get().module.group,
|
||||
deps.kotlin.coroutines.android.get().module.name
|
||||
)
|
||||
}
|
||||
|
||||
/** Compose */
|
||||
implementation(deps.compose.constraintLayout)
|
||||
|
|
@ -40,11 +43,12 @@ dependencies {
|
|||
|
||||
/** Other libraries */
|
||||
implementation(deps.compose.accompanist.systemUiController)
|
||||
implementation(deps.compose.accompanist.permission)
|
||||
implementation(deps.material)
|
||||
implementation(deps.compose.shimmer)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.zxing.qrCore)
|
||||
implementation(deps.jodatime)
|
||||
api(deps.jodatime)
|
||||
implementation(deps.timber)
|
||||
implementation(deps.markdown)
|
||||
}
|
||||
|
|
@ -1,11 +1,19 @@
|
|||
package com.tangem.core.ui
|
||||
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.ui.haptic.HapticManager
|
||||
import com.tangem.core.ui.message.EventMessageHandler
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
|
||||
@Stable
|
||||
interface UiDependencies {
|
||||
|
||||
val hapticManager: HapticManager
|
||||
|
||||
val appThemeModeHolder: AppThemeModeHolder
|
||||
|
||||
val globalSnackbarHostState: SnackbarHostState
|
||||
|
||||
val eventMessageHandler: EventMessageHandler
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* A composable that draws a fade effect at the bottom of the screen. Used on screens with a list of repeating
|
||||
* elements and floating button at the bottom of the screen.
|
||||
*/
|
||||
@Composable
|
||||
fun BottomFade(modifier: Modifier = Modifier, backgroundColor: Color = TangemTheme.colors.background.secondary) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size100 + bottomBarHeight)
|
||||
.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
backgroundColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
fun BoxWithGradient(
|
||||
modifier: Modifier = Modifier,
|
||||
gradient: Brush = BottomGradient,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
val bottomInsetsPx = WindowInsets.navigationBars.getBottom(LocalDensity.current)
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
content()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size164 + bottomInsetsPx.dp)
|
||||
.background(gradient),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val BottomGradient: Brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
TangemColorPalette.Black.copy(alpha = 0f),
|
||||
TangemColorPalette.Black.copy(alpha = 0.75f),
|
||||
TangemColorPalette.Black.copy(alpha = 0.95f),
|
||||
TangemColorPalette.Black,
|
||||
),
|
||||
)
|
||||
|
|
@ -86,6 +86,7 @@ fun PrimaryButton(
|
|||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
size: TangemButtonSize = TangemButtonSize.Default,
|
||||
colors: ButtonColors = TangemButtonsDefaults.primaryButtonColors,
|
||||
showProgress: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
|
|
@ -94,7 +95,7 @@ fun PrimaryButton(
|
|||
text = text,
|
||||
icon = TangemButtonIconPosition.None,
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.primaryButtonColors,
|
||||
colors = colors,
|
||||
enabled = enabled,
|
||||
showProgress = showProgress,
|
||||
size = size,
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.components.SelctorDialogParamsProvider.SelectorDialogParams
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
||||
import com.tangem.core.ui.components.fields.SimpleDialogTextField
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -100,7 +100,7 @@ fun TextInputDialog(
|
|||
confirmButton: DialogButton,
|
||||
onDismissDialog: () -> Unit,
|
||||
onValueChange: (TextFieldValue) -> Unit,
|
||||
textFieldParams: AdditionalTextInputDialogParams,
|
||||
textFieldParams: AdditionalTextInputDialogParams = remember { AdditionalTextInputDialogParams() },
|
||||
title: String? = null,
|
||||
dismissButton: DialogButton? = null,
|
||||
isDismissable: Boolean = true,
|
||||
|
|
@ -128,7 +128,7 @@ fun TextInputDialog(
|
|||
confirmButton: DialogButton,
|
||||
onDismissDialog: () -> Unit,
|
||||
onValueChange: (String) -> Unit,
|
||||
textFieldParams: AdditionalTextInputDialogParams,
|
||||
textFieldParams: AdditionalTextInputDialogParams = remember { AdditionalTextInputDialogParams() },
|
||||
title: String? = null,
|
||||
dismissButton: DialogButton? = null,
|
||||
isDismissable: Boolean = true,
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
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,61 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.tangem.core.ui.res.LocalIsInDarkTheme
|
||||
|
||||
/**
|
||||
* Provides the ability to set a scrim for 3-button navigation
|
||||
*
|
||||
* Automatically makes navigation bar transparent when the composable is disposed.
|
||||
*
|
||||
* Usage example: [com.tangem.feature.tokendetails.presentation.TokenDetailsFragment]
|
||||
*/
|
||||
@Composable
|
||||
fun NavigationBar3ButtonsScrim() {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val isDarkTheme = LocalIsInDarkTheme.current
|
||||
SideEffect {
|
||||
systemUiController.isNavigationBarContrastEnforced = true
|
||||
}
|
||||
LaunchedEffect(systemUiController.isNavigationBarContrastEnforced) {
|
||||
if (systemUiController.isNavigationBarContrastEnforced.not()) {
|
||||
systemUiController.isNavigationBarContrastEnforced = true
|
||||
}
|
||||
}
|
||||
DisposableEffect(isDarkTheme) {
|
||||
onDispose {
|
||||
systemUiController.isNavigationBarContrastEnforced = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to set dark/light icons in cases where the darkness of the screen differs from that
|
||||
* provided in [TangemTheme].
|
||||
*
|
||||
* Automatically returns the icon colors to their original state when the composable is disposed.
|
||||
*
|
||||
* Usage example: [com.tangem.feature.qrscanning.QrScanningFragment]
|
||||
*/
|
||||
@Composable
|
||||
fun SystemBarsIconsDisposable(darkIcons: Boolean, isNavigationBarContrastEnforced: Boolean = false) {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
|
||||
SideEffect {
|
||||
systemUiController.systemBarsDarkContentEnabled = darkIcons
|
||||
systemUiController.isNavigationBarContrastEnforced = isNavigationBarContrastEnforced
|
||||
}
|
||||
|
||||
val isDarkTheme = LocalIsInDarkTheme.current
|
||||
|
||||
DisposableEffect(isDarkTheme) {
|
||||
onDispose {
|
||||
systemUiController.systemBarsDarkContentEnabled = !isDarkTheme
|
||||
systemUiController.isNavigationBarContrastEnforced = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,57 @@
|
|||
package com.tangem.core.ui.components.appbar
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.appbar.models.AdditionalButton
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* App bar with title and two additional buttons
|
||||
*
|
||||
* @param startButton button information attached to the left edge
|
||||
* @param endButton button information attached to the right edge
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=50%3A403&t=tRQ4KlzkjV7TCLZl-4">
|
||||
* Figma Component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun AppBarWithAdditionalButtons(
|
||||
text: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
startButton: AdditionalButton? = null,
|
||||
endButton: AdditionalButton? = null,
|
||||
textColor: Color = TangemTheme.colors.text.primary1,
|
||||
iconColor: Color = TangemTheme.colors.icon.primary1,
|
||||
) {
|
||||
AppBarWithAdditionalButtons(
|
||||
text = text.resolveReference(),
|
||||
startButton = startButton,
|
||||
endButton = endButton,
|
||||
modifier = modifier,
|
||||
textColor = textColor,
|
||||
iconColor = iconColor,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* App bar with title and two additional buttons
|
||||
|
|
@ -31,43 +65,63 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
@Composable
|
||||
fun AppBarWithAdditionalButtons(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
startButton: AdditionalButton? = null,
|
||||
endButton: AdditionalButton? = null,
|
||||
textColor: Color = TangemTheme.colors.text.primary1,
|
||||
iconColor: Color = TangemTheme.colors.icon.primary1,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size56)
|
||||
.padding(all = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
if (startButton != null) {
|
||||
IconButton(modifier = Modifier.align(Alignment.CenterStart), onClick = startButton.onIconClicked) {
|
||||
Icon(
|
||||
painter = painterResource(id = startButton.iconRes),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
painter = painterResource(id = startButton.iconRes),
|
||||
contentDescription = null,
|
||||
tint = iconColor,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size24)
|
||||
.align(Alignment.CenterStart)
|
||||
.clickable(
|
||||
onClick = startButton.onIconClicked,
|
||||
role = Role.Button,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = androidx.compose.material.ripple.rememberRipple(
|
||||
bounded = false,
|
||||
radius = TangemTheme.dimens.size24 / 2,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
)
|
||||
|
||||
if (endButton != null) {
|
||||
IconButton(modifier = Modifier.align(Alignment.CenterEnd), onClick = endButton.onIconClicked) {
|
||||
Icon(
|
||||
painter = painterResource(id = endButton.iconRes),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
painter = painterResource(id = endButton.iconRes),
|
||||
contentDescription = null,
|
||||
tint = iconColor,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size24)
|
||||
.align(Alignment.CenterEnd)
|
||||
.clickable(
|
||||
onClick = endButton.onIconClicked,
|
||||
role = Role.Button,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = androidx.compose.material.ripple.rememberRipple(
|
||||
bounded = false,
|
||||
radius = TangemTheme.dimens.size24 / 2,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -87,6 +141,7 @@ private fun Preview_AppBarWithAdditionalButtons() {
|
|||
iconRes = R.drawable.ic_more_vertical_24,
|
||||
onIconClicked = {},
|
||||
),
|
||||
modifier = Modifier.background(TangemTheme.colors.background.secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -102,6 +157,7 @@ private fun Preview_AppBarWithOnlyStartButtons() {
|
|||
iconRes = R.drawable.ic_scan_24,
|
||||
onIconClicked = {},
|
||||
),
|
||||
modifier = Modifier.background(TangemTheme.colors.background.secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -117,6 +173,7 @@ private fun Preview_AppBarWithOnlyEndButtons() {
|
|||
iconRes = R.drawable.ic_more_vertical_24,
|
||||
onIconClicked = {},
|
||||
),
|
||||
modifier = Modifier.background(TangemTheme.colors.background.secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package com.tangem.core.ui.components.appbar.models
|
||||
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
private const val COLLAPSED_APP_BAR_THRESHOLD = 0.4f
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TopAppBarMedium(
|
||||
title: TextReference,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
navigationIconResId: Int = R.drawable.ic_back_24,
|
||||
colors: TopAppBarColors = TangemTopAppBarColors,
|
||||
) {
|
||||
MediumTopAppBar(
|
||||
modifier = modifier,
|
||||
scrollBehavior = scrollBehavior,
|
||||
colors = colors,
|
||||
title = {
|
||||
val collapsedStyle = TangemTheme.typography.subtitle1
|
||||
val expandedStyle = TangemTheme.typography.h1
|
||||
val style by remember(scrollBehavior.state.collapsedFraction) {
|
||||
derivedStateOf {
|
||||
if (scrollBehavior.state.collapsedFraction >= COLLAPSED_APP_BAR_THRESHOLD) {
|
||||
collapsedStyle
|
||||
} else {
|
||||
expandedStyle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = style,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onBackClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = navigationIconResId),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
internal val TangemTopAppBarColors: TopAppBarColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = TopAppBarColors(
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
scrolledContainerColor = TangemTheme.colors.background.secondary,
|
||||
navigationIconContentColor = TangemTheme.colors.icon.primary1,
|
||||
titleContentColor = TangemTheme.colors.text.primary1,
|
||||
actionIconContentColor = TangemTheme.colors.icon.primary1,
|
||||
)
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package com.tangem.core.ui.components.atoms.radiobutton
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
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
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* [Radio button](https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=101-119&t=FH84ljLBk1vmGAei-4)
|
||||
*
|
||||
* @param isSelected Whether the radio button is selected
|
||||
* @param onClick Called when the user clicks the button
|
||||
* @param modifier Modifier to be applied to the button
|
||||
* @param isEnabled Whether the button click is enabled
|
||||
*/
|
||||
@Composable
|
||||
fun TangemRadioButton(
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
isEnabled: Boolean = true,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.clickable(
|
||||
enabled = isEnabled,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(bounded = false, radius = TangemTheme.dimens.size16),
|
||||
onClick = onClick,
|
||||
),
|
||||
) {
|
||||
val color = TangemTheme.colors.stroke.secondary
|
||||
val radius = with(LocalDensity.current) { TangemTheme.dimens.size9.toPx() }
|
||||
val width = with(LocalDensity.current) { TangemTheme.dimens.size2.toPx() }
|
||||
Canvas(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size24)
|
||||
.padding(TangemTheme.dimens.spacing2),
|
||||
) {
|
||||
drawCircle(
|
||||
color = color,
|
||||
radius = radius,
|
||||
style = Stroke(width),
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = isSelected,
|
||||
label = "Radio button animation",
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size24),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_check_circle_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.control.checked,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TangemRadioButton_Preview() {
|
||||
var isSelected by remember { mutableStateOf(false) }
|
||||
TangemThemePreview {
|
||||
TangemRadioButton(isSelected, onClick = { isSelected = !isSelected })
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.core.ui.components.block
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
fun BlockCard(
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
colors: CardColors = TangemBlockCardColors,
|
||||
onClick: () -> Unit = {},
|
||||
content: @Composable ColumnScope.() -> Unit = {},
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
colors = colors,
|
||||
enabled = enabled,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
val TangemBlockCardColors: CardColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = CardColors(
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
disabledContainerColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tangem.core.ui.components.block
|
||||
|
||||
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.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.components.block.model.BlockUM
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
fun BlockItem(model: BlockUM, modifier: Modifier = Modifier) {
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
onClick = model.onClick,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12, Alignment.Start),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = model.iconRes),
|
||||
tint = when (model.accentType) {
|
||||
BlockUM.AccentType.NONE -> TangemTheme.colors.icon.secondary
|
||||
BlockUM.AccentType.ACCENT -> TangemTheme.colors.text.accent
|
||||
BlockUM.AccentType.WARNING -> TangemTheme.colors.text.warning
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = model.text.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = when (model.accentType) {
|
||||
BlockUM.AccentType.NONE -> TangemTheme.colors.text.primary1
|
||||
BlockUM.AccentType.ACCENT -> TangemTheme.colors.text.accent
|
||||
BlockUM.AccentType.WARNING -> TangemTheme.colors.text.warning
|
||||
},
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.core.ui.components.block.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class BlockUM(
|
||||
val text: TextReference,
|
||||
@DrawableRes val iconRes: Int,
|
||||
val onClick: () -> Unit,
|
||||
val accentType: AccentType = AccentType.NONE,
|
||||
) {
|
||||
|
||||
enum class AccentType {
|
||||
NONE, ACCENT, WARNING,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
package com.tangem.core.ui.components.bottomsheets
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import com.tangem.core.ui.res.LocalWindowSize
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ import kotlinx.coroutines.launch
|
|||
* Tangem bottom sheet with custom draggable header and config
|
||||
*
|
||||
* @param config data model containing logic and ui models
|
||||
* @param
|
||||
* @param content custom bottom sheet content
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
@ -22,20 +24,33 @@ import kotlinx.coroutines.launch
|
|||
inline fun <reified T : TangemBottomSheetConfigContent> TangemBottomSheet(
|
||||
config: TangemBottomSheetConfig,
|
||||
containerColor: Color = TangemTheme.colors.background.primary,
|
||||
addBottomInsets: Boolean = true,
|
||||
crossinline content: @Composable ColumnScope.(T) -> Unit,
|
||||
) {
|
||||
var isVisible by remember { mutableStateOf(value = config.isShow) }
|
||||
|
||||
val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() }
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
if (isVisible && config.content is T) {
|
||||
ModalBottomSheet(
|
||||
// FIXME temporary solution to fix height of the bottom sheet
|
||||
modifier = Modifier.sizeIn(maxHeight = LocalWindowSize.current.height - statusBarHeight),
|
||||
onDismissRequest = config.onDismissRequest,
|
||||
sheetState = sheetState,
|
||||
containerColor = containerColor,
|
||||
shape = TangemTheme.shapes.bottomSheetLarge,
|
||||
windowInsets = WindowInsetsZero,
|
||||
dragHandle = { TangemBottomSheetDraggableHeader(color = containerColor) },
|
||||
) {
|
||||
content(config.content)
|
||||
if (addBottomInsets) {
|
||||
Column(
|
||||
// FIXME temporary solution to fix height of the bottom sheet
|
||||
modifier = Modifier.navigationBarsPadding(),
|
||||
) {
|
||||
content(config.content)
|
||||
}
|
||||
} else {
|
||||
content(config.content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@ import androidx.compose.foundation.LocalIndication
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
|
|
@ -21,8 +18,9 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -42,7 +40,7 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
*/
|
||||
@Composable
|
||||
inline fun <reified T> SegmentedButtons(
|
||||
config: PersistentList<T>,
|
||||
config: ImmutableList<T>,
|
||||
crossinline onClick: (T) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = TangemTheme.colors.background.tertiary,
|
||||
|
|
@ -51,7 +49,7 @@ inline fun <reified T> SegmentedButtons(
|
|||
showIndication: Boolean = true,
|
||||
initialSelectedItem: T? = null,
|
||||
isEnabled: Boolean = true,
|
||||
crossinline buttonContent: @Composable (T) -> Unit,
|
||||
crossinline buttonContent: @Composable BoxScope.(T) -> Unit,
|
||||
) {
|
||||
if (config.isEmpty() || config.size == 1) return
|
||||
|
||||
|
|
@ -97,7 +95,7 @@ inline fun <reified T> SegmentedButtons(
|
|||
onClick(config[index])
|
||||
},
|
||||
) {
|
||||
buttonContent.invoke(config[index])
|
||||
buttonContent.invoke(this, config[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -14,18 +13,15 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.currency.tokenicon.LoadingIcon
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.components.inputrow.inner.InputRowAsyncImage
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -63,7 +59,7 @@ fun InputRowBestRate(
|
|||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
InnerIcon(imageUrl = imageUrl)
|
||||
InputRowAsyncImage(imageUrl = imageUrl, modifier = Modifier.size(TangemTheme.dimens.spacing40))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing12),
|
||||
|
|
@ -131,29 +127,6 @@ private fun InnerTitle(title: TextReference, titleExtra: TextReference, showTag:
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InnerIcon(imageUrl: String) {
|
||||
SubcomposeAsyncImage(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size40),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(imageUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(enable = false)
|
||||
.build(),
|
||||
loading = { LoadingIcon() },
|
||||
error = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.tertiary,
|
||||
shape = CircleShape,
|
||||
),
|
||||
)
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
//region preview
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.core.ui.components.inputrow
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.tangem.core.ui.components.inputrow.inner.InputRowAsyncImage
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun InputRowImageBase(
|
||||
subtitle: TextReference,
|
||||
caption: TextReference,
|
||||
imageUrl: String,
|
||||
modifier: Modifier = Modifier,
|
||||
subtitleColor: Color = TangemTheme.colors.text.primary1,
|
||||
captionColor: Color = TangemTheme.colors.text.tertiary,
|
||||
extraContent: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier,
|
||||
) {
|
||||
InputRowAsyncImage(
|
||||
imageUrl = imageUrl,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.spacing36)
|
||||
.padding(vertical = TangemTheme.dimens.size1),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Text(
|
||||
text = subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = subtitleColor,
|
||||
)
|
||||
Text(
|
||||
text = caption.resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = captionColor,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing2),
|
||||
)
|
||||
}
|
||||
extraContent()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.core.ui.components.inputrow
|
||||
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* Input row component with selector
|
||||
* [Input Row Image](https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=2100-842&t=hoBXmDX8NeLrp4p6-4)
|
||||
*
|
||||
* @param subtitle subtitle text
|
||||
* @param caption caption text
|
||||
* @param imageUrl icon to load
|
||||
* @param modifier modifier
|
||||
* @param subtitleColor subtitle text color
|
||||
* @param captionColor caption text color
|
||||
*/
|
||||
@Composable
|
||||
fun InputRowImageChevron(
|
||||
subtitle: TextReference,
|
||||
caption: TextReference,
|
||||
imageUrl: String,
|
||||
modifier: Modifier = Modifier,
|
||||
subtitleColor: Color = TangemTheme.colors.text.primary1,
|
||||
captionColor: Color = TangemTheme.colors.text.tertiary,
|
||||
) {
|
||||
InputRowImageBase(
|
||||
subtitle = subtitle,
|
||||
caption = caption,
|
||||
imageUrl = imageUrl,
|
||||
modifier = modifier,
|
||||
subtitleColor = subtitleColor,
|
||||
captionColor = captionColor,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
package com.tangem.core.ui.components.inputrow
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.atoms.radiobutton.TangemRadioButton
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.annotatedReference
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Input row component with selector
|
||||
* [Input Row Image](https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=2772-905&t=FH84ljLBk1vmGAei-4)
|
||||
*
|
||||
* @param subtitle subtitle text
|
||||
* @param caption caption text
|
||||
* @param imageUrl icon to load
|
||||
* @param onSelect callback when selected
|
||||
* @param modifier modifier
|
||||
* @param subtitleColor subtitle text color
|
||||
* @param captionColor caption text color
|
||||
* @param isSelected true if selected
|
||||
*/
|
||||
@Composable
|
||||
fun InputRowImageSelector(
|
||||
subtitle: TextReference,
|
||||
caption: TextReference,
|
||||
imageUrl: String,
|
||||
onSelect: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
subtitleColor: Color = TangemTheme.colors.text.primary1,
|
||||
captionColor: Color = TangemTheme.colors.text.tertiary,
|
||||
isSelected: Boolean = false,
|
||||
) {
|
||||
InputRowImageBase(
|
||||
subtitle = subtitle,
|
||||
caption = caption,
|
||||
imageUrl = imageUrl,
|
||||
subtitleColor = subtitleColor,
|
||||
captionColor = captionColor,
|
||||
modifier = modifier
|
||||
.clickable(
|
||||
onClick = onSelect,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(),
|
||||
)
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
TangemRadioButton(isSelected = isSelected, isEnabled = false, onClick = onSelect)
|
||||
}
|
||||
}
|
||||
|
||||
//region preview
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun InputRowImageSelectorPreview(
|
||||
@PreviewParameter(InputRowImageSelectorPreviewDataProvider::class) data: InputRowImageSelectorPreviewData,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
InputRowImageSelector(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.action),
|
||||
subtitle = data.subtitle,
|
||||
caption = combinedReference(
|
||||
resourceReference(R.string.staking_details_apr),
|
||||
annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append(" ")
|
||||
withStyle(style = SpanStyle(color = TangemTheme.colors.text.accent)) {
|
||||
append(
|
||||
BigDecimalFormatter.formatPercent(BigDecimal.ZERO, true),
|
||||
)
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
imageUrl = "",
|
||||
isSelected = false,
|
||||
onSelect = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private data class InputRowImageSelectorPreviewData(
|
||||
val subtitle: TextReference,
|
||||
val caption: TextReference,
|
||||
val showDivider: Boolean,
|
||||
val actionIconRes: Int?,
|
||||
val isSelected: Boolean,
|
||||
)
|
||||
|
||||
private class InputRowImageSelectorPreviewDataProvider :
|
||||
PreviewParameterProvider<InputRowImageSelectorPreviewData> {
|
||||
override val values: Sequence<InputRowImageSelectorPreviewData>
|
||||
get() = sequenceOf(
|
||||
InputRowImageSelectorPreviewData(
|
||||
subtitle = TextReference.Str("subtitle"),
|
||||
caption = TextReference.Str("caption"),
|
||||
actionIconRes = null,
|
||||
showDivider = false,
|
||||
isSelected = false,
|
||||
),
|
||||
InputRowImageSelectorPreviewData(
|
||||
subtitle = TextReference.Str("subtitle"),
|
||||
caption = TextReference.Str("caption"),
|
||||
actionIconRes = R.drawable.ic_chevron_right_24,
|
||||
showDivider = true,
|
||||
isSelected = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.core.ui.components.inputrow.inner
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.currency.tokenicon.LoadingIcon
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* Loads image by url for icon in the input row
|
||||
*
|
||||
* @param imageUrl url of the image
|
||||
* @param modifier modifier
|
||||
*/
|
||||
@Composable
|
||||
internal fun InputRowAsyncImage(imageUrl: String, modifier: Modifier = Modifier) {
|
||||
SubcomposeAsyncImage(
|
||||
modifier = modifier,
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(imageUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(enable = false)
|
||||
.build(),
|
||||
loading = { LoadingIcon() },
|
||||
error = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.tertiary,
|
||||
shape = CircleShape,
|
||||
),
|
||||
)
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemColorPalette.White
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Travala notification with image background
|
||||
|
|
@ -192,7 +193,7 @@ private fun formatSubtitle(subtitle: String): AnnotatedString {
|
|||
@Preview
|
||||
@Composable
|
||||
private fun TravalaNotificationWithBackgroundPreview() {
|
||||
TangemTheme {
|
||||
TangemThemePreview {
|
||||
TravalaNotificationWithBackground(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,147 @@
|
|||
package com.tangem.core.ui.components.rows
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.R
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
fun RoundableCornersRow(
|
||||
startText: String,
|
||||
startTextColor: Color,
|
||||
startTextStyle: TextStyle,
|
||||
endText: String,
|
||||
endTextColor: Color,
|
||||
endTextStyle: TextStyle,
|
||||
cornersToRound: CornersToRound,
|
||||
iconResId: Int? = null,
|
||||
) {
|
||||
Surface(
|
||||
shape = cornersToRound.getShape(),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size48)
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = startText,
|
||||
color = startTextColor,
|
||||
maxLines = 1,
|
||||
style = startTextStyle,
|
||||
)
|
||||
if (iconResId != null) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing4)
|
||||
.size(TangemTheme.dimens.size16),
|
||||
painter = painterResource(id = R.drawable.ic_alert_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
text = endText,
|
||||
color = endTextColor,
|
||||
maxLines = 1,
|
||||
style = endTextStyle,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class CornersToRound {
|
||||
|
||||
ALL_4,
|
||||
TOP_2,
|
||||
BOTTOM_2,
|
||||
ZERO,
|
||||
;
|
||||
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
fun getShape(): RoundedCornerShape {
|
||||
val radius = TangemTheme.dimens.radius12
|
||||
return when (this) {
|
||||
ALL_4 -> RoundedCornerShape(radius)
|
||||
TOP_2 -> RoundedCornerShape(topStart = radius, topEnd = radius)
|
||||
BOTTOM_2 -> RoundedCornerShape(bottomStart = radius, bottomEnd = radius)
|
||||
ZERO -> RoundedCornerShape(0.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_RoundableCornersRow(
|
||||
@PreviewParameter(RoundableCornersRowDataProvider::class) previewData: RoundableCornersRowPreviewData,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
Box(modifier = Modifier.background(color = TangemTheme.colors.icon.attention)) {
|
||||
RoundableCornersRow(
|
||||
startText = previewData.startText,
|
||||
startTextColor = TangemTheme.colors.text.tertiary,
|
||||
startTextStyle = TangemTheme.typography.subtitle2,
|
||||
endText = previewData.endText,
|
||||
endTextColor = TangemTheme.colors.text.primary1,
|
||||
endTextStyle = TangemTheme.typography.subtitle2,
|
||||
cornersToRound = previewData.cornersToRound,
|
||||
iconResId = previewData.iconResId,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class RoundableCornersRowPreviewData(
|
||||
val startText: String,
|
||||
val endText: String,
|
||||
val cornersToRound: CornersToRound,
|
||||
val iconResId: Int? = null,
|
||||
)
|
||||
|
||||
private class RoundableCornersRowDataProvider :
|
||||
PreviewParameterProvider<RoundableCornersRowPreviewData> {
|
||||
|
||||
override val values: Sequence<RoundableCornersRowPreviewData>
|
||||
get() = sequenceOf(
|
||||
getPreviewData(cornersToRound = CornersToRound.ZERO),
|
||||
getPreviewData(cornersToRound = CornersToRound.TOP_2),
|
||||
getPreviewData(cornersToRound = CornersToRound.BOTTOM_2),
|
||||
getPreviewData(cornersToRound = CornersToRound.ZERO, iconResId = R.drawable.ic_alert_24),
|
||||
getPreviewData(cornersToRound = CornersToRound.TOP_2, iconResId = R.drawable.ic_alert_24),
|
||||
getPreviewData(cornersToRound = CornersToRound.BOTTOM_2, iconResId = R.drawable.ic_alert_24),
|
||||
)
|
||||
|
||||
private fun getPreviewData(cornersToRound: CornersToRound, iconResId: Int? = null) = RoundableCornersRowPreviewData(
|
||||
startText = "startText",
|
||||
endText = "endText",
|
||||
cornersToRound = cornersToRound,
|
||||
iconResId = iconResId,
|
||||
)
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.utils.Strings
|
||||
|
||||
@Composable
|
||||
fun SelectorRowItem(
|
||||
|
|
@ -127,7 +128,7 @@ private fun RowScope.SelectorValueContent(
|
|||
)
|
||||
if (postDot != null) {
|
||||
Text(
|
||||
text = "•",
|
||||
text = Strings.DOT,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
package com.tangem.core.ui.components.showcase
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.background
|
||||
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.material3.Text
|
||||
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.R
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.showcase.model.ShowcaseButtonModel
|
||||
import com.tangem.core.ui.components.showcase.model.ShowcaseItemModel
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* @param headerIconRes big header icon
|
||||
* @param headerText header text
|
||||
* @param showcaseItems list of bullet points
|
||||
* @param primaryButton primary button
|
||||
* @param secondaryButton secondary button
|
||||
* @param modifier compose modifier
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/design/Vs6SkVsFnUPsSCNwlnVf5U/Android-%E2%80%93-UI?node-id=12620-90568&t=WeCOVTgeYfkr24Ff-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun Showcase(
|
||||
@DrawableRes headerIconRes: Int,
|
||||
headerText: TextReference,
|
||||
showcaseItems: ImmutableList<ShowcaseItemModel>,
|
||||
primaryButton: ShowcaseButtonModel,
|
||||
secondaryButton: ShowcaseButtonModel,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
ShowcaseContent(
|
||||
headerIconRes = headerIconRes,
|
||||
headerText = headerText,
|
||||
showcaseItems = showcaseItems,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
ShowcaseButtons(
|
||||
primaryButtonText = primaryButton.buttonText,
|
||||
onPrimaryClick = primaryButton.onClick,
|
||||
secondaryButtonText = secondaryButton.buttonText,
|
||||
onSecondaryClick = secondaryButton.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShowcaseButtons(
|
||||
primaryButtonText: TextReference,
|
||||
secondaryButtonText: TextReference,
|
||||
onPrimaryClick: () -> Unit,
|
||||
onSecondaryClick: () -> Unit,
|
||||
hint: TextReference? = null,
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
PrimaryButton(
|
||||
text = primaryButtonText.resolveReference(),
|
||||
onClick = onPrimaryClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
)
|
||||
SecondaryButton(
|
||||
text = secondaryButtonText.resolveReference(),
|
||||
onClick = onSecondaryClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing12,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
)
|
||||
hint?.let {
|
||||
Text(
|
||||
text = it.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 720)
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 720, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Showcase_Preview() {
|
||||
TangemThemePreview {
|
||||
Showcase(
|
||||
headerIconRes = R.drawable.ic_notifications_unread_24,
|
||||
headerText = resourceReference(R.string.user_push_notification_agreement_header),
|
||||
showcaseItems = persistentListOf(
|
||||
ShowcaseItemModel(
|
||||
R.drawable.ic_rocket_launch_24,
|
||||
resourceReference(R.string.user_push_notification_agreement_argument_one),
|
||||
),
|
||||
ShowcaseItemModel(
|
||||
R.drawable.ic_storefront_24,
|
||||
resourceReference(R.string.user_push_notification_agreement_argument_two),
|
||||
),
|
||||
),
|
||||
primaryButton = ShowcaseButtonModel(resourceReference(R.string.common_allow), {}),
|
||||
secondaryButton = ShowcaseButtonModel(resourceReference(R.string.common_later), {}),
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package com.tangem.core.ui.components.showcase
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.core.ui.components.showcase.model.ShowcaseItemModel
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun ShowcaseContent(
|
||||
@DrawableRes headerIconRes: Int,
|
||||
headerText: TextReference,
|
||||
showcaseItems: ImmutableList<ShowcaseItemModel>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = headerIconRes),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.size(TangemTheme.dimens.size56),
|
||||
)
|
||||
Text(
|
||||
text = headerText.resolveReference(),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing34,
|
||||
end = TangemTheme.dimens.spacing34,
|
||||
top = TangemTheme.dimens.spacing28,
|
||||
),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing34,
|
||||
end = TangemTheme.dimens.spacing34,
|
||||
top = TangemTheme.dimens.spacing28,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
repeat(showcaseItems.size) { index ->
|
||||
ShowcaseItem(
|
||||
iconRes = showcaseItems[index].iconRes,
|
||||
text = showcaseItems[index].text,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.core.ui.components.showcase
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun ShowcaseItem(@DrawableRes iconRes: Int, text: TextReference) {
|
||||
Row {
|
||||
Icon(
|
||||
painter = painterResource(id = iconRes),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
)
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing20),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.ui.components.showcase.model
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class ShowcaseButtonModel(
|
||||
val buttonText: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.core.ui.components.showcase.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class ShowcaseItemModel(
|
||||
@DrawableRes val iconRes: Int,
|
||||
val text: TextReference,
|
||||
)
|
||||
|
|
@ -19,6 +19,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Snackbar to inform the user about copying text to the clipboard
|
||||
|
|
@ -100,7 +101,7 @@ private fun MessageText(text: TextReference, modifier: Modifier = Modifier) {
|
|||
private fun Preview_CopiedTextSnackbar(
|
||||
@PreviewParameter(CopiedTextSnackbarDataProvider::class) message: TextReference,
|
||||
) {
|
||||
TangemTheme(isDark = false) {
|
||||
TangemThemePreview {
|
||||
CopiedTextSnackbar(message = message)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import com.tangem.common.Strings
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
|
|
@ -35,6 +34,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.utils.Strings
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.core.ui.decompose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Stable
|
||||
fun interface ComposableContentComponent {
|
||||
|
||||
@Composable
|
||||
@Suppress("TopLevelComposableFunctions") // TODO: Remove this check
|
||||
fun Content(modifier: Modifier)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.core.ui.decompose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
|
||||
@Stable
|
||||
interface ComposableDialogComponent {
|
||||
|
||||
val doOnDismiss: () -> Unit
|
||||
|
||||
@Composable
|
||||
@Suppress("TopLevelComposableFunctions") // TODO: Remove this check
|
||||
fun Dialog()
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import org.intellij.markdown.MarkdownElementTypes
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* Utility class for creating text as [String] or [StringRes].
|
||||
|
|
@ -46,6 +48,13 @@ sealed interface TextReference {
|
|||
*/
|
||||
data class Str(val value: String) : TextReference
|
||||
|
||||
/**
|
||||
* Annotated text
|
||||
*
|
||||
* @property value annotated string
|
||||
*/
|
||||
data class Annotated(val value: AnnotatedString) : TextReference
|
||||
|
||||
/**
|
||||
* Combined reference. It concatenates all [refs].
|
||||
*
|
||||
|
|
@ -81,6 +90,16 @@ fun stringReference(value: String): TextReference {
|
|||
return TextReference.Str(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [TextReference] using an annotated string value.
|
||||
*
|
||||
* @param value The annotated string value.
|
||||
* @return A [TextReference] representing the provided annotated string value.
|
||||
*/
|
||||
fun annotatedReference(value: AnnotatedString): TextReference {
|
||||
return TextReference.Annotated(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [TextReference] using a plural string resource ID with count and optional format arguments.
|
||||
*
|
||||
|
|
@ -131,6 +150,7 @@ fun TextReference.resolveReference(): String {
|
|||
}
|
||||
is TextReference.PluralRes -> pluralStringResource(id, count, *formatArgs.toTypedArray())
|
||||
is TextReference.Str -> value
|
||||
is TextReference.Annotated -> value.text
|
||||
is TextReference.Combined -> {
|
||||
buildString {
|
||||
refs.forEach {
|
||||
|
|
@ -153,6 +173,7 @@ fun TextReference.resolveReference(resources: Resources): String {
|
|||
}
|
||||
is TextReference.PluralRes -> resources.getQuantityString(id, count, *formatArgs.toTypedArray())
|
||||
is TextReference.Str -> value
|
||||
is TextReference.Annotated -> value.text
|
||||
is TextReference.Combined -> {
|
||||
buildString {
|
||||
refs.forEach {
|
||||
|
|
@ -178,9 +199,10 @@ fun TextReference.resolveAnnotatedReference(): AnnotatedString {
|
|||
pluralStringResource(id, count, *formatArgs.toTypedArray()),
|
||||
)
|
||||
is TextReference.Str -> formatAnnotated(value)
|
||||
is TextReference.Annotated -> value
|
||||
is TextReference.Combined -> buildAnnotatedString {
|
||||
refs.forEach {
|
||||
append(formatAnnotated(it.resolveReference()))
|
||||
append(it.resolveAnnotatedReference())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -193,10 +215,21 @@ operator fun TextReference.plus(ref: TextReference): TextReference {
|
|||
is TextReference.PluralRes,
|
||||
is TextReference.Res,
|
||||
is TextReference.Str,
|
||||
is TextReference.Annotated,
|
||||
-> TextReference.Combined(refs = wrappedList(this, ref))
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun TextReference?.isNullOrEmpty(): Boolean {
|
||||
contract {
|
||||
returns(false) implies (this@isNullOrEmpty != null)
|
||||
}
|
||||
|
||||
return this == null || this == TextReference.EMPTY
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun formatAnnotated(rawString: String): AnnotatedString {
|
||||
val markdownDescriptor = rememberMarkdownParser()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.core.ui.message
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.ui.UiMessage
|
||||
import com.tangem.core.decompose.ui.UiMessageHandler
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
|
|
@ -11,6 +12,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
/**
|
||||
* Message handler that is used to show or remove an [EventMessage] in the UI.
|
||||
*/
|
||||
@Stable
|
||||
class EventMessageHandler(
|
||||
private val events: MutableStateFlow<StateEvent<EventMessage>> = MutableStateFlow(consumedEvent()),
|
||||
) : UiMessageHandler, StateFlow<StateEvent<EventMessage>> by events {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ class TangemColors internal constructor(
|
|||
warning: Color,
|
||||
attention: Color,
|
||||
accent: Color = TangemColorPalette.Azure,
|
||||
constant: Color = TangemColorPalette.White,
|
||||
) {
|
||||
var primary1 by mutableStateOf(primary1)
|
||||
private set
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ data class TangemDimens internal constructor(
|
|||
val size5: Dp = 5.dp,
|
||||
val size7: Dp = 7.dp,
|
||||
val size8: Dp = 8.dp,
|
||||
val size9: Dp = 9.dp,
|
||||
val size10: Dp = 10.dp,
|
||||
val size11: Dp = 11.dp,
|
||||
val size12: Dp = 12.dp,
|
||||
|
|
@ -81,8 +82,10 @@ data class TangemDimens internal constructor(
|
|||
val size90: Dp = 90.dp,
|
||||
val size93: Dp = 93.dp,
|
||||
val size96: Dp = 96.dp,
|
||||
val size100: Dp = 100.dp,
|
||||
val size102: Dp = 102.dp,
|
||||
val size108: Dp = 108.dp,
|
||||
val size110: Dp = 110.dp,
|
||||
val size116: Dp = 116.dp,
|
||||
val size120: Dp = 120.dp,
|
||||
val size142: Dp = 142.dp,
|
||||
|
|
@ -99,9 +102,11 @@ data class TangemDimens internal constructor(
|
|||
val spacing2: Dp = 2.dp,
|
||||
val spacing3: Dp = 3.dp,
|
||||
val spacing4: Dp = 4.dp,
|
||||
val spacing5: Dp = 5.dp,
|
||||
val spacing6: Dp = 6.dp,
|
||||
val spacing8: Dp = 8.dp,
|
||||
val spacing10: Dp = 10.dp,
|
||||
val spacing11: Dp = 11.dp,
|
||||
val spacing12: Dp = 12.dp,
|
||||
val spacing14: Dp = 14.dp,
|
||||
val spacing15: Dp = 15.dp,
|
||||
|
|
|
|||
|
|
@ -3,19 +3,22 @@ package com.tangem.core.ui.res
|
|||
import androidx.compose.material.Colors
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.ProvideTextStyle
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.tangem.core.ui.haptic.HapticManager
|
||||
import com.tangem.core.ui.haptic.MockHapticManager
|
||||
|
||||
// TODO: use isSystemInDarkTheme() for automatic color detection
|
||||
internal const val IS_SYSTEM_IN_DARK_THEME: Boolean = false
|
||||
import com.tangem.core.ui.windowsize.WindowSize
|
||||
|
||||
@Composable
|
||||
fun TangemTheme(
|
||||
isDark: Boolean = false,
|
||||
windowSize: WindowSize,
|
||||
typography: TangemTypography = TangemTheme.typography,
|
||||
dimens: TangemDimens = TangemTheme.dimens,
|
||||
hapticManager: HapticManager = MockHapticManager,
|
||||
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val themeColors = if (isDark) darkThemeColors() else lightThemeColors()
|
||||
|
|
@ -23,6 +26,16 @@ fun TangemTheme(
|
|||
.also { it.update(themeColors) }
|
||||
|
||||
val shapes = remember { TangemShapes(dimens) }
|
||||
val systemUiController = rememberSystemUiController()
|
||||
|
||||
SideEffect {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = !isDark,
|
||||
isNavigationBarContrastEnforced = false,
|
||||
)
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colors = materialThemeColors(colors = themeColors, isDark = isDark),
|
||||
) {
|
||||
|
|
@ -33,6 +46,8 @@ fun TangemTheme(
|
|||
LocalTangemShapes provides shapes,
|
||||
LocalIsInDarkTheme provides isDark,
|
||||
LocalHapticManager provides hapticManager,
|
||||
LocalSnackbarHostState provides snackbarHostState,
|
||||
LocalWindowSize provides windowSize,
|
||||
) {
|
||||
ProvideTextStyle(
|
||||
value = TangemTheme.typography.body1,
|
||||
|
|
@ -204,4 +219,12 @@ val LocalIsInDarkTheme = staticCompositionLocalOf { false }
|
|||
|
||||
val LocalHapticManager = staticCompositionLocalOf<HapticManager> {
|
||||
error("No HapticManager provided")
|
||||
}
|
||||
|
||||
val LocalSnackbarHostState = staticCompositionLocalOf<SnackbarHostState> {
|
||||
error("No SnackbarHostState provided")
|
||||
}
|
||||
|
||||
val LocalWindowSize = staticCompositionLocalOf<WindowSize> {
|
||||
error("No WindowSize provided")
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.core.ui.res
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tangem.core.ui.windowsize.rememberWindowSizePreview
|
||||
|
||||
@Composable
|
||||
fun TangemThemePreview(
|
||||
|
|
@ -12,10 +14,13 @@ fun TangemThemePreview(
|
|||
) {
|
||||
val isDarkTheme = isDark ?: isSystemInDarkTheme()
|
||||
|
||||
TangemTheme(
|
||||
isDark = isDarkTheme,
|
||||
typography = typography,
|
||||
dimens = dimens,
|
||||
content = content,
|
||||
)
|
||||
BoxWithConstraints {
|
||||
TangemTheme(
|
||||
isDark = isDarkTheme,
|
||||
typography = typography,
|
||||
dimens = dimens,
|
||||
windowSize = rememberWindowSizePreview(maxWidth, maxHeight),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,6 @@ abstract class ComposeActivity : ComponentActivity(), ComposeScreen {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(createComposeView(context = this))
|
||||
setContentView(createComposeView(context = this, activity = this))
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ abstract class ComposeBottomSheetFragment : BottomSheetDialogFragment(), Compose
|
|||
override fun getTheme(): Int = R.style.AppTheme_TransparentBottomSheetDialog
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return createComposeView(inflater.context)
|
||||
return createComposeView(inflater.context, requireActivity())
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ abstract class ComposeFragment : Fragment(), ComposeScreen {
|
|||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val isTransitionsInflated = TransitionInflater.from(requireContext()).inflateTransitions()
|
||||
|
||||
return createComposeView(inflater.context).also {
|
||||
return createComposeView(inflater.context, requireActivity()).also {
|
||||
it.isTransitionGroup = isTransitionsInflated
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.core.ui.screen
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
|
|
@ -10,6 +11,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.ComposeView
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.windowsize.rememberWindowSize
|
||||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
|
||||
/**
|
||||
|
|
@ -50,14 +52,17 @@ internal interface ComposeScreen {
|
|||
* @param context The context.
|
||||
* @return A [ComposeView] instance with the defined screen content.
|
||||
*/
|
||||
internal fun ComposeScreen.createComposeView(context: Context): ComposeView {
|
||||
internal fun ComposeScreen.createComposeView(context: Context, activity: Activity): ComposeView {
|
||||
return ComposeView(context).apply {
|
||||
setContent {
|
||||
val appThemeMode by uiDependencies.appThemeModeHolder.appThemeMode
|
||||
val windowSize = rememberWindowSize(activity = activity)
|
||||
|
||||
TangemTheme(
|
||||
isDark = shouldUseDarkTheme(appThemeMode),
|
||||
windowSize = windowSize,
|
||||
hapticManager = uiDependencies.hapticManager,
|
||||
snackbarHostState = uiDependencies.globalSnackbarHostState,
|
||||
) {
|
||||
ScreenContent(modifier = screenModifier)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.google.accompanist.permissions.shouldShowRationale
|
||||
|
||||
/**
|
||||
* Returns push permission requester.
|
||||
* Handles granting permission from app settings.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
fun requestPushPermission(
|
||||
isFirstTimeAsking: Boolean,
|
||||
pushPermission: String?,
|
||||
isClicked: MutableState<Boolean>,
|
||||
onAllow: () -> Unit,
|
||||
onDeny: () -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
): () -> Unit {
|
||||
val permissionState = pushPermission?.let { permission ->
|
||||
val tempPermissionState = rememberPermissionState(permission = permission)
|
||||
rememberPermissionState(permission = permission) {
|
||||
when {
|
||||
it -> onAllow()
|
||||
!tempPermissionState.status.shouldShowRationale && !isFirstTimeAsking -> onOpenSettings()
|
||||
else -> onDeny()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user granted permission and close bottom sheet
|
||||
LaunchedEffect(key1 = permissionState?.status, isClicked) {
|
||||
if (!isClicked.value) return@LaunchedEffect
|
||||
if (permissionState?.status?.isGranted == true) {
|
||||
onAllow()
|
||||
} else {
|
||||
onDeny()
|
||||
}
|
||||
}
|
||||
|
||||
return if (permissionState == null) {
|
||||
onOpenSettings
|
||||
} else {
|
||||
permissionState::launchPermissionRequest
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
|
||||
object WindowInsetsZero : WindowInsets {
|
||||
override fun getBottom(density: Density): Int = 0
|
||||
override fun getLeft(density: Density, layoutDirection: LayoutDirection): Int = 0
|
||||
override fun getRight(density: Density, layoutDirection: LayoutDirection): Int = 0
|
||||
override fun getTop(density: Density): Int = 0
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package com.tangem.core.ui.windowsize
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.toComposeRect
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.window.layout.WindowMetricsCalculator
|
||||
|
||||
/**
|
||||
* The preferred way to determine the window size is because it provides the actual size of the window as opposed to
|
||||
* [LocalConfiguration.current.screenHeightDp]
|
||||
*
|
||||
* @property width actual window width
|
||||
* @property height actual window height
|
||||
* @property widthSizeType window size type based on width
|
||||
* @property heightSizeType window size type based on height
|
||||
* @property smallestSize smallest size of width and height
|
||||
*/
|
||||
data class WindowSize(
|
||||
val width: Dp,
|
||||
val height: Dp,
|
||||
) {
|
||||
val widthSizeType: WindowSizeType = getWidthWindowSizeType(width)
|
||||
val heightSizeType: WindowSizeType = getHeightWindowSizeType(height)
|
||||
val smallestSize: Dp = minOf(width, height)
|
||||
|
||||
fun widthAtLeast(type: WindowSizeType): Boolean = this.widthSizeType.ordinal >= type.ordinal
|
||||
|
||||
fun widthAtLeast(value: Dp): Boolean = this.width >= value
|
||||
|
||||
fun heightAtLeast(type: WindowSizeType): Boolean = this.heightSizeType.ordinal >= type.ordinal
|
||||
|
||||
fun heightAtLeast(value: Dp): Boolean = this.height >= value
|
||||
|
||||
fun widthAtMost(type: WindowSizeType): Boolean = this.widthSizeType.ordinal <= type.ordinal
|
||||
|
||||
fun widthAtMost(value: Dp): Boolean = this.width <= value
|
||||
|
||||
fun heightAtMost(type: WindowSizeType): Boolean = this.heightSizeType.ordinal <= type.ordinal
|
||||
|
||||
fun heightAtMost(value: Dp): Boolean = this.height <= value
|
||||
|
||||
private fun getWidthWindowSizeType(windowDp: Dp): WindowSizeType = when {
|
||||
windowDp < 300.dp -> WindowSizeType.ExtraSmall
|
||||
windowDp < 380.dp -> WindowSizeType.Small
|
||||
windowDp < 540.dp -> WindowSizeType.Normal
|
||||
windowDp < 700.dp -> WindowSizeType.Large
|
||||
else -> WindowSizeType.ExtraLarge
|
||||
}
|
||||
|
||||
private fun getHeightWindowSizeType(heightDp: Dp): WindowSizeType = when {
|
||||
heightDp < 480.dp -> WindowSizeType.ExtraSmall
|
||||
heightDp < 640.dp -> WindowSizeType.Small
|
||||
heightDp < 860.dp -> WindowSizeType.Normal
|
||||
heightDp < 1100.dp -> WindowSizeType.Large
|
||||
else -> WindowSizeType.ExtraLarge
|
||||
}
|
||||
}
|
||||
|
||||
enum class WindowSizeType {
|
||||
ExtraSmall, Small, Normal, Large, ExtraLarge
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberWindowSize(activity: Activity): WindowSize {
|
||||
val windowBoundsSize = rememberWindowBoundsSize(activity)
|
||||
val windowDpSize = with(LocalDensity.current) {
|
||||
windowBoundsSize.toDpSize()
|
||||
}
|
||||
|
||||
return WindowSize(
|
||||
width = windowDpSize.width,
|
||||
height = windowDpSize.height,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun rememberWindowSizePreview(width: Dp, height: Dp): WindowSize {
|
||||
return remember(width, height) {
|
||||
WindowSize(
|
||||
width = width,
|
||||
height = height,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberWindowBoundsSize(activity: Activity): Size {
|
||||
val configuration = LocalConfiguration.current
|
||||
val windowMetrics = remember(configuration) {
|
||||
WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity)
|
||||
}
|
||||
return windowMetrics.bounds.toComposeRect().size
|
||||
}
|
||||
5
core/ui/src/main/res/drawable/ic_check_circle_24.xml
Normal file
5
core/ui/src/main/res/drawable/ic_check_circle_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="#1E1E1E" android:pathData="M12,2C6.5,2 2,6.5 2,12C2,17.5 6.5,22 12,22C17.5,22 22,17.5 22,12C22,6.5 17.5,2 12,2ZM10,17L5,12L6.41,10.59L10,14.17L17.59,6.58L19,8L10,17Z"/>
|
||||
|
||||
</vector>
|
||||
11
core/ui/src/main/res/drawable/ic_notifications_unread_24.xml
Normal file
11
core/ui/src/main/res/drawable/ic_notifications_unread_24.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<group>
|
||||
|
||||
<clip-path android:pathData="M0,0h24v24h-24z"/>
|
||||
|
||||
<path android:fillColor="#1E1E1E" android:pathData="M11.998,21.692C11.501,21.692 11.075,21.515 10.722,21.161C10.369,20.807 10.192,20.382 10.192,19.885H13.808C13.808,20.383 13.631,20.809 13.276,21.163C12.922,21.516 12.496,21.692 11.998,21.692ZM4.5,18.885V17.385H6.308V9.923C6.308,8.578 6.723,7.389 7.553,6.356C8.383,5.322 9.449,4.661 10.75,4.373V3.75C10.75,3.403 10.871,3.108 11.114,2.865C11.357,2.622 11.652,2.5 11.999,2.5C12.346,2.5 12.641,2.622 12.884,2.865C13.128,3.108 13.25,3.403 13.25,3.75V3.95C13.086,4.246 12.956,4.547 12.859,4.854C12.763,5.16 12.704,5.478 12.683,5.808C12.569,5.787 12.458,5.769 12.35,5.754C12.242,5.738 12.125,5.731 12,5.731C10.842,5.731 9.854,6.14 9.035,6.959C8.217,7.777 7.808,8.765 7.808,9.923V17.385H16.192V11.133C16.422,11.215 16.664,11.279 16.919,11.325C17.174,11.371 17.432,11.391 17.692,11.385V17.385H19.5V18.885H4.5ZM17.482,9.202C16.718,9.202 16.069,8.935 15.534,8.4C14.998,7.866 14.731,7.217 14.731,6.453C14.731,5.689 14.998,5.04 15.533,4.505C16.067,3.97 16.716,3.702 17.479,3.702C18.243,3.702 18.893,3.969 19.428,4.504C19.963,5.038 20.231,5.687 20.231,6.451C20.231,7.214 19.963,7.864 19.429,8.399C18.895,8.934 18.245,9.202 17.482,9.202Z"/>
|
||||
|
||||
</group>
|
||||
|
||||
</vector>
|
||||
11
core/ui/src/main/res/drawable/ic_rocket_launch_24.xml
Normal file
11
core/ui/src/main/res/drawable/ic_rocket_launch_24.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<group>
|
||||
|
||||
<clip-path android:pathData="M0,0h24v24h-24z"/>
|
||||
|
||||
<path android:fillColor="#1E1E1E" android:pathData="M5.65,10.025L7.6,10.85C7.833,10.383 8.075,9.933 8.325,9.5C8.575,9.067 8.85,8.633 9.15,8.2L7.75,7.925L5.65,10.025ZM9.2,12.1L12.05,14.925C12.75,14.658 13.5,14.25 14.3,13.7C15.1,13.15 15.85,12.525 16.55,11.825C17.717,10.658 18.629,9.363 19.288,7.938C19.946,6.512 20.233,5.2 20.15,4C18.95,3.917 17.633,4.204 16.2,4.863C14.767,5.521 13.467,6.433 12.3,7.6C11.6,8.3 10.975,9.05 10.425,9.85C9.875,10.65 9.467,11.4 9.2,12.1ZM13.65,10.475C13.267,10.092 13.075,9.621 13.075,9.063C13.075,8.504 13.267,8.033 13.65,7.65C14.033,7.267 14.508,7.075 15.075,7.075C15.642,7.075 16.117,7.267 16.5,7.65C16.883,8.033 17.075,8.504 17.075,9.063C17.075,9.621 16.883,10.092 16.5,10.475C16.117,10.858 15.642,11.05 15.075,11.05C14.508,11.05 14.033,10.858 13.65,10.475ZM14.125,18.5L16.225,16.4L15.95,15C15.517,15.3 15.083,15.571 14.65,15.813C14.217,16.054 13.767,16.292 13.3,16.525L14.125,18.5ZM21.95,2.175C22.267,4.192 22.071,6.154 21.362,8.063C20.654,9.971 19.433,11.792 17.7,13.525L18.2,16C18.267,16.333 18.25,16.658 18.15,16.975C18.05,17.292 17.883,17.567 17.65,17.8L13.45,22L11.35,17.075L7.075,12.8L2.15,10.7L6.325,6.5C6.558,6.267 6.837,6.1 7.162,6C7.487,5.9 7.817,5.883 8.15,5.95L10.625,6.45C12.358,4.717 14.175,3.492 16.075,2.775C17.975,2.058 19.933,1.858 21.95,2.175ZM3.925,15.975C4.508,15.392 5.221,15.096 6.062,15.087C6.904,15.079 7.617,15.367 8.2,15.95C8.783,16.533 9.071,17.246 9.062,18.087C9.054,18.929 8.758,19.642 8.175,20.225C7.758,20.642 7.062,21 6.087,21.3C5.112,21.6 3.767,21.867 2.05,22.1C2.283,20.383 2.55,19.038 2.85,18.063C3.15,17.087 3.508,16.392 3.925,15.975ZM5.35,17.375C5.183,17.542 5.017,17.846 4.85,18.288C4.683,18.729 4.567,19.175 4.5,19.625C4.95,19.558 5.396,19.446 5.837,19.288C6.279,19.129 6.583,18.967 6.75,18.8C6.95,18.6 7.058,18.358 7.075,18.075C7.092,17.792 7,17.55 6.8,17.35C6.6,17.15 6.358,17.054 6.075,17.063C5.792,17.071 5.55,17.175 5.35,17.375Z"/>
|
||||
|
||||
</group>
|
||||
|
||||
</vector>
|
||||
11
core/ui/src/main/res/drawable/ic_storefront_24.xml
Normal file
11
core/ui/src/main/res/drawable/ic_storefront_24.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<group>
|
||||
|
||||
<clip-path android:pathData="M0,0h24v24h-24z"/>
|
||||
|
||||
<path android:fillColor="#1E1E1E" android:pathData="M21.025,11.05V19C21.025,19.55 20.829,20.021 20.438,20.413C20.046,20.804 19.575,21 19.025,21H5.025C4.475,21 4.004,20.804 3.612,20.413C3.221,20.021 3.025,19.55 3.025,19V11.05C2.642,10.7 2.346,10.25 2.137,9.7C1.929,9.15 1.925,8.55 2.125,7.9L3.175,4.5C3.308,4.067 3.546,3.708 3.887,3.425C4.229,3.142 4.625,3 5.075,3H18.975C19.425,3 19.817,3.138 20.15,3.412C20.483,3.688 20.725,4.05 20.875,4.5L21.925,7.9C22.125,8.55 22.121,9.142 21.913,9.675C21.704,10.208 21.408,10.667 21.025,11.05ZM14.225,10C14.675,10 15.017,9.846 15.25,9.538C15.483,9.229 15.575,8.883 15.525,8.5L14.975,5H13.025V8.7C13.025,9.05 13.142,9.354 13.375,9.613C13.608,9.871 13.892,10 14.225,10ZM9.725,10C10.108,10 10.421,9.871 10.663,9.613C10.904,9.354 11.025,9.05 11.025,8.7V5H9.075L8.525,8.5C8.458,8.9 8.546,9.25 8.787,9.55C9.029,9.85 9.342,10 9.725,10ZM5.275,10C5.575,10 5.837,9.892 6.062,9.675C6.287,9.458 6.425,9.183 6.475,8.85L7.025,5H5.075L4.075,8.35C3.975,8.683 4.029,9.042 4.237,9.425C4.446,9.808 4.792,10 5.275,10ZM18.775,10C19.258,10 19.608,9.808 19.825,9.425C20.042,9.042 20.092,8.683 19.975,8.35L18.925,5H17.025L17.575,8.85C17.625,9.183 17.763,9.458 17.987,9.675C18.212,9.892 18.475,10 18.775,10ZM5.025,19H19.025V11.95C18.942,11.983 18.888,12 18.862,12H18.775C18.325,12 17.929,11.925 17.587,11.775C17.246,11.625 16.908,11.383 16.575,11.05C16.275,11.35 15.933,11.583 15.55,11.75C15.167,11.917 14.758,12 14.325,12C13.875,12 13.454,11.917 13.063,11.75C12.671,11.583 12.325,11.35 12.025,11.05C11.742,11.35 11.413,11.583 11.038,11.75C10.663,11.917 10.258,12 9.825,12C9.342,12 8.904,11.917 8.512,11.75C8.121,11.583 7.775,11.35 7.475,11.05C7.125,11.4 6.779,11.646 6.437,11.788C6.096,11.929 5.708,12 5.275,12H5.162C5.121,12 5.075,11.983 5.025,11.95V19Z"/>
|
||||
|
||||
</group>
|
||||
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue