Updated on 2026-08-14
This commit is contained in:
parent
7e8b240f98
commit
ce2567f096
5 changed files with 345 additions and 0 deletions
|
|
@ -0,0 +1,114 @@
|
|||
package com.tangem.core.ui.components.snackbar
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.SnackbarData
|
||||
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 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.R
|
||||
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
|
||||
|
||||
/**
|
||||
* Snackbar to inform the user about copying text to the clipboard
|
||||
*
|
||||
* @param message message
|
||||
* @param modifier modifier
|
||||
*
|
||||
* @see <a href = https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=2001-728&t=kDzSZDx0m0sk4iYz-4
|
||||
* >Figma</a>
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun CopiedTextSnackbar(message: TextReference, modifier: Modifier = Modifier) {
|
||||
BaseSnackbar(message = message, modifier = modifier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Snackbar to inform the user about copying text to the clipboard.
|
||||
*
|
||||
* @param snackbarData this is needed to better support Material3.SnackbarHost, but only supports the message field
|
||||
* @param modifier modifier
|
||||
*
|
||||
* @see <a href = https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=2001-728&t=kDzSZDx0m0sk4iYz-4
|
||||
* >Figma</a>
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun CopiedTextSnackbar(snackbarData: SnackbarData, modifier: Modifier = Modifier) {
|
||||
BaseSnackbar(message = stringReference(snackbarData.visuals.message), modifier = modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BaseSnackbar(message: TextReference, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.background(color = TangemTheme.colors.icon.secondary, shape = TangemTheme.shapes.roundedCorners8)
|
||||
.heightIn(min = TangemTheme.dimens.size48)
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing14,
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
MarkIcon()
|
||||
|
||||
MessageText(text = message, modifier = Modifier.weight(weight = 1f, fill = false))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MarkIcon() {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_check_24),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MessageText(text: TextReference, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
modifier = modifier,
|
||||
color = TangemTheme.colors.text.disabled,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(widthDp = 344, showBackground = true, fontScale = 1f)
|
||||
@Preview(widthDp = 344, showBackground = true, fontScale = 1f, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(widthDp = 344, showBackground = true, fontScale = 2f)
|
||||
@Composable
|
||||
private fun Preview_CopiedTextSnackbar(
|
||||
@PreviewParameter(CopiedTextSnackbarDataProvider::class) message: TextReference,
|
||||
) {
|
||||
TangemTheme(isDark = false) {
|
||||
CopiedTextSnackbar(message = message)
|
||||
}
|
||||
}
|
||||
|
||||
private class CopiedTextSnackbarDataProvider : CollectionPreviewParameterProvider<TextReference>(
|
||||
collection = listOf(
|
||||
stringReference(value = "Copied!"),
|
||||
stringReference(value = "Contract address copied!"),
|
||||
stringReference(value = "Coooooooooooontract addreeeeeeeeeeeeeeeess coooooooooooooooopied!"),
|
||||
),
|
||||
)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.core.ui.components.snackbar
|
||||
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
/**
|
||||
* SnackbarHost to inform the user about copying text to the clipboard
|
||||
* Based on Material3 component. It's best way to show [CopiedTextSnackbar].
|
||||
*
|
||||
* @param hostState snackbar host state
|
||||
* @param modifier modifier
|
||||
*
|
||||
* @see <a href = https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=2001-728&t=kDzSZDx0m0sk4iYz-4
|
||||
* >Figma</a>
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun CopiedTextSnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) {
|
||||
SnackbarHost(hostState = hostState, modifier = modifier) {
|
||||
CopiedTextSnackbar(snackbarData = it)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package com.tangem.core.ui.components.snackbar
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Tangem snackbar.
|
||||
* Based on Material3 component. It can be presented as one line or multi lines snackbar – depends on text length.
|
||||
*
|
||||
* @param data snackbar data
|
||||
* @param modifier modifier
|
||||
* @param actionOnNewLine flag that indicates if the action should be displayed on a new line (default: false)
|
||||
*
|
||||
* @see <a href = https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=682-761&t=kDzSZDx0m0sk4iYz-4
|
||||
* >Figma</a>
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun TangemSnackbar(data: SnackbarData, modifier: Modifier = Modifier, actionOnNewLine: Boolean = false) {
|
||||
Snackbar(
|
||||
modifier = modifier,
|
||||
action = {
|
||||
ActionButton(label = data.visuals.actionLabel, onClick = data::performAction)
|
||||
},
|
||||
actionOnNewLine = actionOnNewLine,
|
||||
shape = TangemTheme.shapes.roundedCorners8,
|
||||
containerColor = TangemTheme.colors.icon.secondary,
|
||||
) {
|
||||
MessageText(text = data.visuals.message)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActionButton(label: String?, onClick: () -> Unit) {
|
||||
if (!label.isNullOrBlank()) {
|
||||
TextButton(
|
||||
onClick = onClick,
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = TangemTheme.colors.text.primary2,
|
||||
),
|
||||
content = {
|
||||
Text(
|
||||
text = label,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.button,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MessageText(text: String) {
|
||||
Text(
|
||||
text = text,
|
||||
color = TangemTheme.colors.text.disabled,
|
||||
textAlign = TextAlign.Start,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT!
|
||||
* Preview doesn't work correctly, check on device or start [TangemSnackbarHost]'s preview in interactive mode *
|
||||
*/
|
||||
@Preview(widthDp = 344, showBackground = true, fontScale = 1f)
|
||||
@Preview(widthDp = 344, showBackground = true, fontScale = 1f, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(widthDp = 344, showBackground = true, fontScale = 2f)
|
||||
@Composable
|
||||
private fun Preview_TangemSnackbar(@PreviewParameter(TangemSnackbarModelProvider::class) model: TangemSnackbarModel) {
|
||||
TangemThemePreview {
|
||||
TangemSnackbar(data = model.snackbarData, actionOnNewLine = model.actionOnNewLine)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tangem.core.ui.components.snackbar
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Tangem snackbar host.
|
||||
* Based on Material3 component. It's best way to show [TangemSnackbar].
|
||||
*
|
||||
* @param hostState snackbar host state
|
||||
* @param modifier modifier
|
||||
* @param actionOnNewLine flag that indicates if the action should be displayed on a new line (default: false)
|
||||
*
|
||||
* @see <a href = https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=682-761&t=kDzSZDx0m0sk4iYz-4
|
||||
* >Figma</a>
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun TangemSnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier, actionOnNewLine: Boolean = false) {
|
||||
SnackbarHost(hostState = hostState, modifier = modifier) { data ->
|
||||
TangemSnackbar(data = data, actionOnNewLine = actionOnNewLine)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 344, showBackground = true, fontScale = 1f)
|
||||
@Preview(widthDp = 344, showBackground = true, fontScale = 1f, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(widthDp = 344, showBackground = true, fontScale = 2f)
|
||||
@Composable
|
||||
private fun Preview_TangemSnackbar(@PreviewParameter(TangemSnackbarModelProvider::class) model: TangemSnackbarModel) {
|
||||
TangemThemePreview {
|
||||
val snackbarHostState = remember(::SnackbarHostState)
|
||||
|
||||
TangemSnackbarHost(hostState = snackbarHostState, actionOnNewLine = model.actionOnNewLine)
|
||||
|
||||
LaunchedEffect(key1 = null) {
|
||||
snackbarHostState.showSnackbar(visuals = model.snackbarData.visuals)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package com.tangem.core.ui.components.snackbar
|
||||
|
||||
import androidx.compose.material3.SnackbarData
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.material3.SnackbarVisuals
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
|
||||
internal data class TangemSnackbarModel(val snackbarData: SnackbarData, val actionOnNewLine: Boolean)
|
||||
|
||||
internal class TangemSnackbarModelProvider : CollectionPreviewParameterProvider<TangemSnackbarModel>(
|
||||
listOf(
|
||||
createTangemSnackbarModel(
|
||||
message = "Single-line description.",
|
||||
actionLabel = "Button",
|
||||
actionOnNewLine = false,
|
||||
),
|
||||
createTangemSnackbarModel(
|
||||
message = "Very loooooooooooong single-line description.",
|
||||
actionLabel = "Button",
|
||||
actionOnNewLine = false,
|
||||
),
|
||||
createTangemSnackbarModel(
|
||||
message = "Single-line description.",
|
||||
actionLabel = "Very looooooong button name",
|
||||
actionOnNewLine = false,
|
||||
),
|
||||
createTangemSnackbarModel(
|
||||
message = "Single-line description.",
|
||||
actionLabel = "Button",
|
||||
actionOnNewLine = true,
|
||||
),
|
||||
createTangemSnackbarModel(
|
||||
message = "Very loooooooooooong single-line description.",
|
||||
actionLabel = "Button",
|
||||
actionOnNewLine = true,
|
||||
),
|
||||
createTangemSnackbarModel(
|
||||
message = "Single-line description.",
|
||||
actionLabel = "Very looooooong button name",
|
||||
actionOnNewLine = true,
|
||||
),
|
||||
),
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
fun createTangemSnackbarModel(
|
||||
message: String,
|
||||
actionLabel: String,
|
||||
actionOnNewLine: Boolean,
|
||||
): TangemSnackbarModel {
|
||||
return TangemSnackbarModel(
|
||||
snackbarData = createSnackbarData(message, actionLabel),
|
||||
actionOnNewLine = actionOnNewLine,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createSnackbarData(message: String, actionLabel: String): SnackbarData {
|
||||
return object : SnackbarData {
|
||||
|
||||
override val visuals: SnackbarVisuals
|
||||
get() = object : SnackbarVisuals {
|
||||
override val message: String = message
|
||||
override val actionLabel: String = actionLabel
|
||||
override val duration: SnackbarDuration = SnackbarDuration.Short // Never-mind
|
||||
override val withDismissAction: Boolean = false // Never-mind
|
||||
}
|
||||
|
||||
override fun dismiss() = Unit
|
||||
override fun performAction() = Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue