Updated on 2026-08-14
This commit is contained in:
commit
b40934f2b7
14 changed files with 1168 additions and 0 deletions
|
|
@ -49,6 +49,31 @@ fun Modifier.hazeEffectTangem(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a haze foreground effect to the [Modifier] with consideration of power saving mode.
|
||||
*
|
||||
* @param style The [HazeStyle] to apply. Defaults to [HazeStyle.Unspecified].
|
||||
* @param isBlurEnabled A Boolean indicating whether blur is enabled. Defaults to true.
|
||||
* @param configure A lambda to configure the [HazeEffectScope].
|
||||
* @return A [Modifier] with the configured haze foreground effect applied.
|
||||
*/
|
||||
@Composable
|
||||
fun Modifier.hazeForegroundEffectTangem(
|
||||
style: HazeStyle = HazeStyle.Unspecified,
|
||||
isBlurEnabled: Boolean = true,
|
||||
configure: HazeEffectScope.() -> Unit = {},
|
||||
): Modifier {
|
||||
val powerSavingEnabled = LocalPowerSavingState.current.isPowerSavingModeEnabled.collectAsState()
|
||||
val isGlobalBlurEnabled = isBlurEnabled && !powerSavingEnabled.value
|
||||
|
||||
return hazeEffect(
|
||||
style = style,
|
||||
) {
|
||||
blurEnabled = isGlobalBlurEnabled
|
||||
configure()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a haze source to the [Modifier] using the current global haze state.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -20,6 +20,27 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
/**
|
||||
* [Accent Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8004-26798)
|
||||
*
|
||||
* @param buttonUM TangemButtonUM data model containing button properties.
|
||||
* @param modifier Modifier to be applied to the button.
|
||||
*/
|
||||
@Composable
|
||||
fun AccentTangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifier) {
|
||||
AccentTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Accent Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8004-26798)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -19,6 +19,26 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
/**
|
||||
* [Ghost Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=5854-4804)
|
||||
*
|
||||
* @param buttonUM TangemButtonUM data model containing button properties.
|
||||
* @param modifier Modifier to be applied to the button.
|
||||
*/
|
||||
@Composable
|
||||
fun GhostTangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifier) {
|
||||
GhostTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Ghost Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=5854-4804)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -21,6 +21,27 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
/**
|
||||
* [Outline Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=5854-4800)
|
||||
*
|
||||
* @param buttonUM TangemButtonUM data model containing button properties.
|
||||
* @param modifier Modifier to be applied to the button.
|
||||
*/
|
||||
@Composable
|
||||
fun OutlineTangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifier) {
|
||||
OutlineTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Outline Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=5854-4800)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -20,6 +20,27 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
/**
|
||||
* [Primary Inverse Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=7545-78314)
|
||||
*
|
||||
* @param buttonUM TangemButtonUM data model containing button properties.
|
||||
* @param modifier Modifier to be applied to the button.
|
||||
*/
|
||||
@Composable
|
||||
fun PrimaryInverseTangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifier) {
|
||||
PrimaryInverseTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Primary Inverse Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=7545-78314)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -20,6 +20,28 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
/**
|
||||
* [Primary Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=5854-4732&t=euYo1qCxPlQl3Fa6-4)
|
||||
*
|
||||
* @param buttonUM TangemButtonUM data model containing button properties.
|
||||
* @param modifier Modifier to be applied to the button.
|
||||
*/
|
||||
@Composable
|
||||
fun PrimaryTangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifier) {
|
||||
PrimaryTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
descriptionText = buttonUM.descriptionText,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Primary Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=5854-4732&t=euYo1qCxPlQl3Fa6-4)
|
||||
*
|
||||
|
|
@ -40,6 +62,7 @@ fun PrimaryTangemButton(
|
|||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
text: TextReference? = null,
|
||||
descriptionText: TextReference? = null,
|
||||
@DrawableRes iconRes: Int? = null,
|
||||
iconPosition: TangemButtonIconPosition = TangemButtonIconPosition.Start,
|
||||
enabled: Boolean = true,
|
||||
|
|
@ -66,6 +89,7 @@ fun PrimaryTangemButton(
|
|||
.clip(shape.toShape(size))
|
||||
.then(backgroundModifier),
|
||||
text = text,
|
||||
descriptionText = descriptionText,
|
||||
contentColor = contentColor,
|
||||
enabled = enabled,
|
||||
size = size,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,27 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
/**
|
||||
* [Secondary Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=5854-4796)
|
||||
*
|
||||
* @param buttonUM TangemButtonUM data model containing button properties.
|
||||
* @param modifier Modifier to be applied to the button.
|
||||
*/
|
||||
@Composable
|
||||
fun SecondaryTangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifier) {
|
||||
SecondaryTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Secondary Tangem button](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=5854-4796)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
package com.tangem.core.ui.ds.button
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
/**
|
||||
* Tangem button that selects the appropriate button style based on the provided [TangemButtonUM].
|
||||
* [Button]("https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8320-14720&m=dev)
|
||||
*
|
||||
* @param buttonUM TangemButtonUM data model containing button properties.
|
||||
* @param modifier Modifier to be applied to the button.
|
||||
*
|
||||
* @see [TangemButtonInternal] for the underlying implementation.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun TangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifier) {
|
||||
when (buttonUM.type) {
|
||||
TangemButtonType.Primary -> PrimaryTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
descriptionText = buttonUM.descriptionText,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
TangemButtonType.Secondary -> SecondaryTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
TangemButtonType.Accent -> AccentTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
TangemButtonType.Outline -> OutlineTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
TangemButtonType.PrimaryInverse -> PrimaryInverseTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
shape = buttonUM.shape,
|
||||
)
|
||||
TangemButtonType.Ghost -> GhostTangemButton(
|
||||
onClick = buttonUM.onClick,
|
||||
modifier = modifier,
|
||||
text = buttonUM.text,
|
||||
iconRes = buttonUM.iconRes,
|
||||
iconPosition = buttonUM.iconPosition,
|
||||
enabled = buttonUM.isEnabled,
|
||||
size = buttonUM.size,
|
||||
state = buttonUM.state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ internal fun TangemButtonInternal(
|
|||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
text: TextReference? = null,
|
||||
descriptionText: TextReference? = null,
|
||||
@DrawableRes iconRes: Int? = null,
|
||||
iconPosition: TangemButtonIconPosition = TangemButtonIconPosition.Start,
|
||||
enabled: Boolean = true,
|
||||
|
|
@ -100,6 +101,24 @@ internal fun TangemButtonInternal(
|
|||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(descriptionText != null && state != TangemButtonState.Loading) {
|
||||
val wrappedText = remember(this) { requireNotNull(descriptionText) }
|
||||
val textStyle = TangemTheme.typography2.captionSemibold12
|
||||
Text(
|
||||
text = wrappedText.resolveReference(),
|
||||
style = textStyle,
|
||||
color = TangemTheme.colors2.text.status.disabled,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
autoSize = TextAutoSize.StepBased(
|
||||
minFontSize = 12.sp,
|
||||
maxFontSize = textStyle.fontSize,
|
||||
),
|
||||
modifier = Modifier.testTag(BaseButtonTestTags.TEXT),
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = iconRes != null && iconPosition == TangemButtonIconPosition.End,
|
||||
modifier = Modifier.size(size = size.toContentSize()),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.core.ui.ds.button
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
/**
|
||||
* Data model representing the properties of a Tangem button.
|
||||
*
|
||||
* @param text TextReference for the button label.
|
||||
* @param descriptionText TextReference for the button description (optional).
|
||||
* @param iconRes Drawable resource ID for the icon to be displayed in the button (optional).
|
||||
* @param iconPosition Position of the icon (Start or End).
|
||||
* @param isEnabled Boolean indicating whether the button is enabled.
|
||||
* @param size TangemButtonSize defining the size of the button.
|
||||
* @param state TangemButtonState defining the current state of the button.
|
||||
* @param shape TangemButtonShape defining the shape of the button.
|
||||
* @param type TangemButtonType defining the style type of the button.
|
||||
* @param onClick Lambda to be invoked when the button is clicked.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class TangemButtonUM(
|
||||
val text: TextReference? = null,
|
||||
val descriptionText: TextReference? = null,
|
||||
@DrawableRes val iconRes: Int? = null,
|
||||
val iconPosition: TangemButtonIconPosition = TangemButtonIconPosition.Start,
|
||||
val isEnabled: Boolean = true,
|
||||
val size: TangemButtonSize = TangemButtonSize.X15,
|
||||
val state: TangemButtonState = TangemButtonState.Default,
|
||||
val shape: TangemButtonShape = TangemButtonShape.Default,
|
||||
val type: TangemButtonType,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
/** Enum class representing the style types of Tangem buttons */
|
||||
enum class TangemButtonType {
|
||||
Primary,
|
||||
Secondary,
|
||||
Accent,
|
||||
Outline,
|
||||
PrimaryInverse,
|
||||
Ghost,
|
||||
}
|
||||
|
|
@ -0,0 +1,514 @@
|
|||
package com.tangem.core.ui.ds.message
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
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 androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.flicker
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
|
||||
import com.tangem.core.ui.ds.button.*
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Tangem message component that displays a message based on the provided [TangemMessageUM].
|
||||
* [Message](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8455-81318&m=dev)
|
||||
*
|
||||
* @param messageUM Data model containing message properties.
|
||||
* @param modifier Modifier to be applied to the message component.
|
||||
* @param content Optional composable content to be displayed alongside the title and subtitle.
|
||||
*/
|
||||
@Composable
|
||||
fun TangemMessage(
|
||||
messageUM: TangemMessageUM,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable (RowScope.() -> Unit)? = null,
|
||||
) {
|
||||
TangemMessage(
|
||||
modifier = modifier,
|
||||
title = messageUM.title,
|
||||
subtitle = messageUM.subtitle,
|
||||
messageEffect = messageUM.messageEffect,
|
||||
isCentered = messageUM.isCentered,
|
||||
content = content,
|
||||
onCloseClick = messageUM.onCloseClick,
|
||||
buttons = {
|
||||
messageUM.buttonsUM.fastForEach { buttonUM ->
|
||||
TangemButton(
|
||||
buttonUM = buttonUM.tangemButtonUM,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tangem message component that displays a notification based on the provided [NotificationConfig].
|
||||
* [Message](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8455-81318&m=dev)
|
||||
*
|
||||
* @param config Configuration for the notification message.
|
||||
* @param modifier Modifier to be applied to the message component.
|
||||
*
|
||||
* @see NotificationConfig for more details.
|
||||
* @see com.tangem.core.ui.components.notifications.Notification for legacy component.
|
||||
*/
|
||||
@Composable
|
||||
fun TangemMessage(config: NotificationConfig, modifier: Modifier = Modifier) {
|
||||
val buttonState = config.buttonsState
|
||||
TangemMessage(
|
||||
title = config.title,
|
||||
subtitle = config.subtitle,
|
||||
modifier = modifier,
|
||||
content = {
|
||||
val iconTint = when (config.iconTint) {
|
||||
NotificationConfig.IconTint.Unspecified -> null
|
||||
NotificationConfig.IconTint.Accent -> TangemTheme.colors2.graphic.status.accent
|
||||
NotificationConfig.IconTint.Attention -> TangemTheme.colors2.graphic.status.attention
|
||||
NotificationConfig.IconTint.Warning -> TangemTheme.colors2.graphic.status.warning
|
||||
}
|
||||
if (iconTint == null) {
|
||||
Image(
|
||||
painter = painterResource(config.iconResId),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(config.iconSize),
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(config.iconResId),
|
||||
contentDescription = null,
|
||||
tint = iconTint,
|
||||
modifier = Modifier.size(config.iconSize),
|
||||
)
|
||||
}
|
||||
},
|
||||
buttons = if (buttonState != null) {
|
||||
{
|
||||
TangemMessageLegacyButtons(buttonState = buttonState)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tangem message component that displays a message with optional title, subtitle, content, and buttons.
|
||||
* [Message](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8455-81318&m=dev)
|
||||
*
|
||||
* @param modifier Modifier to be applied to the message component.
|
||||
* @param title Optional title of the message.
|
||||
* @param subtitle Optional subtitle of the message.
|
||||
* @param messageEffect Effect to be applied to the message background.
|
||||
* @param content Optional composable content to be displayed alongside the title and subtitle.
|
||||
* @param buttons Optional composable buttons to be displayed below the message.
|
||||
* @param isCentered Flag indicating whether the content should be centered horizontally.
|
||||
*/
|
||||
@Composable
|
||||
fun TangemMessage(
|
||||
modifier: Modifier = Modifier,
|
||||
title: TextReference? = null,
|
||||
subtitle: TextReference? = null,
|
||||
messageEffect: TangemMessageEffect = TangemMessageEffect.None,
|
||||
content: (@Composable RowScope.() -> Unit)? = null,
|
||||
buttons: (@Composable RowScope.() -> Unit)? = null,
|
||||
onCloseClick: (() -> Unit)? = null,
|
||||
isCentered: Boolean = false,
|
||||
) {
|
||||
val alignment = if (isCentered) {
|
||||
Alignment.CenterHorizontally
|
||||
} else {
|
||||
Alignment.Start
|
||||
}
|
||||
Box(modifier = modifier) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.messageEffectBackground(
|
||||
messageEffect = messageEffect,
|
||||
radius = TangemTheme.dimens2.x6,
|
||||
),
|
||||
)
|
||||
Column(
|
||||
horizontalAlignment = alignment,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens2.x3)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
TangemMessageContent(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
alignment = alignment,
|
||||
content = content,
|
||||
)
|
||||
if (buttons != null && !isCentered) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
buttons()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (onCloseClick != null) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_close_new_20),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.text.neutral.secondary,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(TangemTheme.dimens2.x3)
|
||||
.size(TangemTheme.dimens2.x5)
|
||||
.clickableSingle(onClick = onCloseClick),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemMessageContent(
|
||||
title: TextReference? = null,
|
||||
subtitle: TextReference? = null,
|
||||
alignment: Alignment.Horizontal = Alignment.Start,
|
||||
content: (@Composable RowScope.() -> Unit)? = null,
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
modifier = Modifier.padding(TangemTheme.dimens2.x1),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
horizontalAlignment = alignment,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
|
||||
) {
|
||||
if (title != null) {
|
||||
Text(
|
||||
text = title.resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
if (subtitle != null) {
|
||||
Text(
|
||||
text = subtitle.resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
content?.invoke(this)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.TangemMessageLegacyButtons(buttonState: ButtonsState) {
|
||||
when (buttonState) {
|
||||
is ButtonsState.PrimaryButtonConfig -> PrimaryTangemButton(
|
||||
text = buttonState.text,
|
||||
descriptionText = buttonState.additionalText,
|
||||
iconRes = buttonState.iconResId,
|
||||
onClick = buttonState.onClick,
|
||||
iconPosition = TangemButtonIconPosition.End,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
size = TangemButtonSize.X9,
|
||||
state = if (buttonState.shouldShowProgress) {
|
||||
TangemButtonState.Loading
|
||||
} else {
|
||||
TangemButtonState.Default
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
is ButtonsState.SecondaryButtonConfig -> PrimaryInverseTangemButton(
|
||||
text = buttonState.text,
|
||||
iconRes = buttonState.iconResId,
|
||||
onClick = buttonState.onClick,
|
||||
iconPosition = TangemButtonIconPosition.End,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
size = TangemButtonSize.X9,
|
||||
state = if (buttonState.shouldShowProgress) {
|
||||
TangemButtonState.Loading
|
||||
} else {
|
||||
TangemButtonState.Default
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
is ButtonsState.PairButtonsConfig -> {
|
||||
PrimaryInverseTangemButton(
|
||||
text = buttonState.primaryText,
|
||||
onClick = buttonState.onPrimaryClick,
|
||||
iconPosition = TangemButtonIconPosition.End,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
size = TangemButtonSize.X9,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
PrimaryTangemButton(
|
||||
text = buttonState.secondaryText,
|
||||
onClick = buttonState.onSecondaryClick,
|
||||
iconPosition = TangemButtonIconPosition.End,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
size = TangemButtonSize.X9,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
is ButtonsState.SecondaryPairButtonsConfig -> {
|
||||
PrimaryInverseTangemButton(
|
||||
text = buttonState.leftText,
|
||||
onClick = buttonState.onLeftClick,
|
||||
iconPosition = TangemButtonIconPosition.End,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
size = TangemButtonSize.X9,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
PrimaryInverseTangemButton(
|
||||
text = buttonState.rightText,
|
||||
onClick = buttonState.onRightClick,
|
||||
iconPosition = TangemButtonIconPosition.End,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
size = TangemButtonSize.X9,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun TangemMessage_Preview(@PreviewParameter(TangemMessagePreviewProvider::class) params: TangemMessageUM) {
|
||||
TangemThemePreviewRedesign {
|
||||
TangemMessage(
|
||||
messageUM = params,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TangemMessagePreviewProvider : PreviewParameterProvider<TangemMessageUM> {
|
||||
override val values: Sequence<TangemMessageUM>
|
||||
get() = sequenceOf(
|
||||
TangemMessageUM(
|
||||
title = stringReference("Title text"),
|
||||
subtitle = stringReference("Subtext"),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
isCentered = true,
|
||||
),
|
||||
TangemMessageUM(
|
||||
title = stringReference("Title text"),
|
||||
subtitle = stringReference("Subtext"),
|
||||
messageEffect = TangemMessageEffect.Magic,
|
||||
isCentered = false,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = stringReference("Button"),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = {},
|
||||
),
|
||||
TangemMessageButtonUM(
|
||||
text = stringReference("Button"),
|
||||
type = TangemButtonType.Primary,
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
),
|
||||
TangemMessageUM(
|
||||
title = stringReference("Title text"),
|
||||
subtitle = stringReference("Subtext"),
|
||||
messageEffect = TangemMessageEffect.Card,
|
||||
isCentered = false,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = stringReference("Button"),
|
||||
type = TangemButtonType.Primary,
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
),
|
||||
TangemMessageUM(
|
||||
title = stringReference("Title text"),
|
||||
subtitle = stringReference("Subtext"),
|
||||
messageEffect = TangemMessageEffect.Warning,
|
||||
isCentered = true,
|
||||
onCloseClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun TangemMessage2_Preview() {
|
||||
TangemThemePreviewRedesign {
|
||||
TangemMessage(
|
||||
title = stringReference("Title text"),
|
||||
subtitle = stringReference("Subtext"),
|
||||
messageEffect = TangemMessageEffect.Magic,
|
||||
isCentered = false,
|
||||
content = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x10)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens2.x2))
|
||||
.flicker(isFlickering = true)
|
||||
.background(TangemTheme.colors2.text.neutral.primary),
|
||||
)
|
||||
},
|
||||
buttons = {
|
||||
PrimaryInverseTangemButton(
|
||||
text = stringReference("Button"),
|
||||
onClick = {},
|
||||
modifier = Modifier.weight(1f),
|
||||
size = TangemButtonSize.X9,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
)
|
||||
PrimaryTangemButton(
|
||||
text = stringReference("Button"),
|
||||
onClick = {},
|
||||
modifier = Modifier.weight(1f),
|
||||
size = TangemButtonSize.X9,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors2.surface.level1)
|
||||
.padding(TangemTheme.dimens2.x1)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Preview Legacy
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun TangemMessageLegacy_Preview(
|
||||
@PreviewParameter(TangemMessageLegacyPreviewProvider::class) params: NotificationConfig,
|
||||
) {
|
||||
TangemThemePreviewRedesign {
|
||||
TangemMessage(
|
||||
config = params,
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors2.surface.level1)
|
||||
.padding(TangemTheme.dimens2.x1)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TangemMessageLegacyPreviewProvider : PreviewParameterProvider<NotificationConfig> {
|
||||
override val values: Sequence<NotificationConfig>
|
||||
get() = sequenceOf(
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Development card"),
|
||||
subtitle = TextReference.Str(
|
||||
value = "The card you scanned is a development card.\nDon’t accept it as a payment.",
|
||||
),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
),
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Some networks are unreachable"),
|
||||
subtitle = TextReference.Str(value = "Check your network connection"),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
),
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Used card"),
|
||||
subtitle = TextReference.Str(value = "The card signed transactions in the past"),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
onClick = {},
|
||||
),
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Your wallet hasn’t been backed up"),
|
||||
subtitle = TextReference.Str(
|
||||
value = "To protect your assets, we advise you to carry out this procedure",
|
||||
),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
buttonsState = ButtonsState.SecondaryButtonConfig(
|
||||
text = TextReference.Str(value = "Start backup process"),
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Some addresses are missing"),
|
||||
subtitle = TextReference.Str(value = "Generate addresses for 2 new networks using your card"),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
buttonsState = ButtonsState.PrimaryButtonConfig(
|
||||
text = TextReference.Str(value = "Generate addresses"),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Rate the app"),
|
||||
subtitle = TextReference.Str(value = "How do you like Tangem?"),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
buttonsState = ButtonsState.PairButtonsConfig(
|
||||
primaryText = TextReference.Str(value = "Love it!"),
|
||||
onPrimaryClick = {},
|
||||
secondaryText = TextReference.Str(value = "Can be better"),
|
||||
onSecondaryClick = {},
|
||||
),
|
||||
),
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Rate the app"),
|
||||
subtitle = TextReference.Str(value = "How do you like Tangem?"),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
buttonsState = ButtonsState.SecondaryPairButtonsConfig(
|
||||
leftText = TextReference.Str(value = "Love it!"),
|
||||
onLeftClick = {},
|
||||
rightText = TextReference.Str(value = "Can be better"),
|
||||
onRightClick = {},
|
||||
),
|
||||
),
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Note top up"),
|
||||
subtitle = TextReference.Str(value = "To activate card top up it with at least 1 XLM"),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
buttonsState = ButtonsState.SecondaryButtonConfig(
|
||||
text = TextReference.Str(value = "Top up card"),
|
||||
onClick = {},
|
||||
),
|
||||
onCloseClick = {},
|
||||
),
|
||||
NotificationConfig(
|
||||
subtitle = resourceReference(id = R.string.information_generated_with_ai),
|
||||
iconResId = R.drawable.ic_magic_28,
|
||||
),
|
||||
NotificationConfig(
|
||||
title = resourceReference(R.string.notification_sepa_title),
|
||||
subtitle = resourceReference(R.string.notification_sepa_text),
|
||||
iconResId = R.drawable.img_notification_sepa,
|
||||
buttonsState = ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.notification_sepa_button),
|
||||
onClick = { },
|
||||
),
|
||||
iconSize = 54.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
package com.tangem.core.ui.ds.message
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.haze.hazeForegroundEffectTangem
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
import com.tangem.core.ui.res.LocalIsInDarkTheme
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import dev.chrisbanes.haze.HazeStyle
|
||||
import dev.chrisbanes.haze.HazeTint
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Different visual effects for [Tangem message component].
|
||||
*/
|
||||
enum class TangemMessageEffect {
|
||||
/** Magic effect with vibrant colors */
|
||||
Magic,
|
||||
/** Card effect with bright colors */
|
||||
Card,
|
||||
/** Warning effect with alert colors */
|
||||
Warning,
|
||||
/** No special effect */
|
||||
None,
|
||||
;
|
||||
|
||||
/** Gets the color gradient based on the effect type and [isInDarkTheme] */
|
||||
@Suppress("MagicNumber")
|
||||
fun getColorGradient(isInDarkTheme: Boolean): ImmutableList<Color> {
|
||||
return when (this) {
|
||||
Magic -> if (isInDarkTheme) {
|
||||
persistentListOf(
|
||||
Color(0x9976E463),
|
||||
Color(0x994A40D3),
|
||||
Color(0x99CC4D8C),
|
||||
Color(0x99E0BC56),
|
||||
)
|
||||
} else {
|
||||
persistentListOf(
|
||||
Color(0xFF76E463),
|
||||
Color(0xFF4A40D3),
|
||||
Color(0xFFCC4D8C),
|
||||
Color(0xFFE0BC56),
|
||||
)
|
||||
}
|
||||
Card -> if (isInDarkTheme) {
|
||||
persistentListOf(
|
||||
Color(0x99D21FF2),
|
||||
Color(0x991319F3),
|
||||
Color(0x99FF005D),
|
||||
Color(0x99EAB216),
|
||||
)
|
||||
} else {
|
||||
persistentListOf(
|
||||
Color(0xFFD21FF2),
|
||||
Color(0xFF1319F3),
|
||||
Color(0xFFFF005D),
|
||||
Color(0xFFEAB216),
|
||||
)
|
||||
}
|
||||
Warning -> if (isInDarkTheme) {
|
||||
persistentListOf(
|
||||
Color(0x59FF0004),
|
||||
Color(0x59FF0004),
|
||||
Color(0x59E0BC56),
|
||||
Color(0x59FF0000),
|
||||
Color(0x59FFACC1),
|
||||
Color(0x59FFACC1),
|
||||
)
|
||||
} else {
|
||||
persistentListOf(
|
||||
Color(0x4DFF0004),
|
||||
Color(0x4DFF0004),
|
||||
Color(0x4DE0BC56),
|
||||
Color(0x4DFF0000),
|
||||
Color(0x4DFFACC1),
|
||||
Color(0x4DFFACC1),
|
||||
)
|
||||
}
|
||||
None -> if (isInDarkTheme) {
|
||||
persistentListOf(
|
||||
Color(0x1AFFFFFF),
|
||||
Color(0x1AFFFFFF),
|
||||
)
|
||||
} else {
|
||||
persistentListOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the gradient tints based on the effect type and [isInDarkTheme] */
|
||||
@Suppress("MagicNumber")
|
||||
fun getGradientTint(isInDarkTheme: Boolean) = when (this) {
|
||||
Magic,
|
||||
Card,
|
||||
-> {
|
||||
listOf(
|
||||
HazeTint(
|
||||
color = Color(0x4DFFFFFF),
|
||||
blendMode = BlendMode.Color,
|
||||
),
|
||||
HazeTint(
|
||||
color = Color(0x80C2C2C2),
|
||||
blendMode = BlendMode.Overlay,
|
||||
),
|
||||
HazeTint(
|
||||
color = Color(0x1FC2C2C2),
|
||||
),
|
||||
)
|
||||
}
|
||||
Warning -> {
|
||||
if (isInDarkTheme) {
|
||||
listOf(
|
||||
HazeTint(
|
||||
color = Color(0x4D7F7F7F),
|
||||
|
||||
blendMode = BlendMode.Luminosity,
|
||||
),
|
||||
HazeTint(
|
||||
color = Color(0x80C2C2C2),
|
||||
blendMode = BlendMode.Overlay,
|
||||
),
|
||||
HazeTint(
|
||||
Color(0x1FFF0000),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
listOf(
|
||||
HazeTint(
|
||||
color = Color(0x0DFEC2C2),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
None -> {
|
||||
listOf(
|
||||
HazeTint(
|
||||
color = if (isInDarkTheme) Color(0x1AFFFFFF) else Color.Transparent,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the border gradient based on the effect type and [isInDarkTheme] */
|
||||
@Suppress("MagicNumber")
|
||||
fun getBorderGradient(isInDarkTheme: Boolean): ImmutableList<Color> {
|
||||
return when (this) {
|
||||
Card,
|
||||
Magic,
|
||||
-> if (isInDarkTheme) {
|
||||
persistentListOf(
|
||||
Color(0x4DFFFFFF),
|
||||
Color(0x17FFFFFF),
|
||||
Color(0x4DFFFFFF),
|
||||
Color(0x17FFFFFF),
|
||||
)
|
||||
} else {
|
||||
persistentListOf(
|
||||
Color(0x0d000000),
|
||||
Color(0x26000000),
|
||||
Color(0x0d000000),
|
||||
Color(0x26000000),
|
||||
)
|
||||
}
|
||||
Warning -> persistentListOf(
|
||||
Color(0x4DE44848),
|
||||
Color(0x17E44848),
|
||||
Color(0x4DE44848),
|
||||
Color(0x17E44848),
|
||||
)
|
||||
None -> if (isInDarkTheme) {
|
||||
persistentListOf()
|
||||
} else {
|
||||
persistentListOf(
|
||||
Color(0x0d000000),
|
||||
Color(0x26000000),
|
||||
Color(0x0d000000),
|
||||
Color(0x26000000),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the border color based on the effect type and [isInDarkTheme] */
|
||||
@Suppress("MagicNumber")
|
||||
fun getBorderColor(isInDarkTheme: Boolean): Color {
|
||||
return when (this) {
|
||||
Card,
|
||||
Magic,
|
||||
None,
|
||||
-> if (isInDarkTheme) Color(0x1AFFFFFF) else Color.Transparent
|
||||
Warning -> Color(0x1AE44848)
|
||||
}
|
||||
}
|
||||
|
||||
// todo redesign replace with proper color when design is ready
|
||||
@Suppress("MagicNumber")
|
||||
fun fallbackColor(isInDarkTheme: Boolean): Color {
|
||||
return when (this) {
|
||||
Card -> Color(0x339B8B86)
|
||||
Magic -> Color(0x33B43B96)
|
||||
Warning -> if (isInDarkTheme) Color(0x33FF0000) else Color(0x33FF0000)
|
||||
None -> if (isInDarkTheme) Color(0x1AFFFFFF) else Color.Transparent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Applies a message effect background to the [Modifier] based on the provided [messageEffect] and [radius] */
|
||||
@Composable
|
||||
internal fun Modifier.messageEffectBackground(messageEffect: TangemMessageEffect, radius: Dp): Modifier {
|
||||
val isInDarkTheme = LocalIsInDarkTheme.current
|
||||
val borderGradientColors = remember { messageEffect.getBorderGradient(isInDarkTheme) }
|
||||
val gradientColors = remember { messageEffect.getColorGradient(isInDarkTheme) }
|
||||
|
||||
return this
|
||||
.clip(RoundedCornerShape(radius))
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = messageEffect.getBorderColor(isInDarkTheme),
|
||||
shape = RoundedCornerShape(radius),
|
||||
)
|
||||
.conditionalCompose(borderGradientColors.isNotEmpty()) {
|
||||
border(
|
||||
width = 1.dp,
|
||||
brush = Brush.sweepGradient(
|
||||
colors = messageEffect.getBorderGradient(isInDarkTheme),
|
||||
center = Offset.Infinite,
|
||||
),
|
||||
shape = RoundedCornerShape(radius),
|
||||
)
|
||||
}
|
||||
.hazeForegroundEffectTangem(
|
||||
style = HazeStyle(tints = messageEffect.getGradientTint(isInDarkTheme)),
|
||||
isBlurEnabled = true,
|
||||
) {
|
||||
fallbackTint = HazeTint(
|
||||
messageEffect.fallbackColor(isInDarkTheme),
|
||||
)
|
||||
blurRadius = 25.dp
|
||||
}
|
||||
.conditionalCompose(gradientColors.isNotEmpty()) {
|
||||
border(
|
||||
width = 0.dp,
|
||||
brush = Brush.sweepGradient(messageEffect.getColorGradient(isInDarkTheme)),
|
||||
shape = RoundedCornerShape(radius),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun TangemMessageEffect_Preview() {
|
||||
TangemThemePreviewRedesign {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors2.surface.level1)
|
||||
.padding(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
TangemMessageEffect.entries.forEach { messageEffect ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.messageEffectBackground(
|
||||
messageEffect = messageEffect,
|
||||
radius = 16.dp,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.height(height = 100.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.core.ui.ds.message
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.ds.button.*
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Data model representing the properties of a Tangem message.
|
||||
*
|
||||
* @param title TextReference for the message title.
|
||||
* @param subtitle TextReference for the message subtitle.
|
||||
* @param messageEffect TangemMessageEffect defining the visual effect of the message.
|
||||
* @param isCentered Boolean indicating whether the icon is centered.
|
||||
* @param buttonsUM ImmutableList of TangemMessageButtonUM representing the buttons in the message.
|
||||
* @param onCloseClick Lambda to be invoked when the close button is clicked (optional
|
||||
*/
|
||||
data class TangemMessageUM(
|
||||
val title: TextReference,
|
||||
val subtitle: TextReference,
|
||||
val messageEffect: TangemMessageEffect,
|
||||
val isCentered: Boolean,
|
||||
val buttonsUM: ImmutableList<TangemMessageButtonUM> = persistentListOf(),
|
||||
val onCloseClick: (() -> Unit)? = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* Data model representing a button within a Tangem message.
|
||||
*
|
||||
* @param text TextReference for the button label.
|
||||
* @param type TangemButtonType defining the style type of the button.
|
||||
* @param iconRes Drawable resource ID for the icon to be displayed in the button (optional).
|
||||
* @param onClick Lambda to be invoked when the button is clicked.
|
||||
*/
|
||||
data class TangemMessageButtonUM(
|
||||
val text: TextReference,
|
||||
val type: TangemButtonType,
|
||||
@DrawableRes val iconRes: Int? = null,
|
||||
val onClick: () -> Unit,
|
||||
) {
|
||||
/** Creates a TangemButtonUM representation of this message button. */
|
||||
val tangemButtonUM = TangemButtonUM(
|
||||
text = text,
|
||||
size = TangemButtonSize.X9,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
iconRes = iconRes,
|
||||
iconPosition = TangemButtonIconPosition.End,
|
||||
type = type,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
9
core/ui/src/main/res/drawable/ic_close_new_20.xml
Normal file
9
core/ui/src/main/res/drawable/ic_close_new_20.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M9.992,2C14.369,2 18,5.624 18,10C18,14.369 14.376,18 10,18C5.631,18 2,14.369 2,10C2,5.624 5.624,2 9.992,2ZM12.651,6.722C12.479,6.722 12.337,6.777 12.204,6.91L10,9.098L7.812,6.918C7.686,6.8 7.545,6.737 7.364,6.737C7.004,6.737 6.722,7.012 6.722,7.381C6.722,7.553 6.785,7.71 6.91,7.835L9.082,10.008L6.91,12.189C6.785,12.306 6.722,12.471 6.722,12.644C6.722,13.004 7.004,13.294 7.364,13.294C7.545,13.294 7.709,13.231 7.835,13.106L10,10.926L12.173,13.106C12.29,13.231 12.455,13.294 12.635,13.294C13.003,13.294 13.294,13.004 13.294,12.644C13.294,12.463 13.231,12.298 13.098,12.181L10.926,10.008L13.106,7.827C13.247,7.686 13.302,7.545 13.302,7.364C13.302,7.004 13.012,6.722 12.651,6.722Z"
|
||||
android:fillColor="#656565"/>
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue