Updated on 2026-08-14
This commit is contained in:
parent
bf56c51cd6
commit
50fa269746
10 changed files with 161 additions and 166 deletions
|
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
|
|
@ -14,7 +15,7 @@ import androidx.compose.ui.graphics.Brush
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -24,25 +25,17 @@ import kotlin.math.cos
|
|||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* Design-system rectangle shimmer placeholder.
|
||||
* Design-system v2 shimmer placeholder — a rounded rectangle with a sweeping highlight.
|
||||
*
|
||||
* A rounded rectangle painted with `bg.opaque.secondary`. A tilted band sweeps across it where
|
||||
* the base color's alpha is gradually dimmed toward the center of the band and restored at the
|
||||
* edges, producing a soft "blade" highlight passing through the placeholder. The alpha profile
|
||||
* matches [com.tangem.core.ui.components.text.BladeAnimation].
|
||||
*
|
||||
* Cycle: 1.5s hold → 0.8s linear sweep → restart.
|
||||
*
|
||||
* Version 1.0
|
||||
* [Figma](https://www.figma.com/design/AsnJ5CPHib4Qxw12gszjMS/%F0%9F%92%A0-DS-Components?node-id=3398-625&p=f&m=dev)
|
||||
*
|
||||
* Sizing is the caller's responsibility — set width and height via [modifier].
|
||||
* For a placeholder sized after a typography line, use the [TangemShimmer] text overload instead.
|
||||
*
|
||||
* @param modifier Modifier applied to the shimmer's root.
|
||||
* @param modifier Modifier applied to the shimmer's root. Set the width and height here.
|
||||
* @param radius Corner radius of the rectangle.
|
||||
*/
|
||||
@Composable
|
||||
fun RectangleShimmer(modifier: Modifier = Modifier, radius: Dp = 6.dp) {
|
||||
fun TangemShimmer(modifier: Modifier = Modifier, radius: Dp = TangemShimmer.DefaultRadius) {
|
||||
val baseColor = TangemTheme.colors3.bg.opaque.secondary
|
||||
val progress = LocalTangemShimmerProgress.current ?: rememberShimmerProgressInstance()
|
||||
val colorStops = remember(baseColor) { buildColorStops(baseColor) }
|
||||
|
|
@ -77,63 +70,76 @@ fun RectangleShimmer(modifier: Modifier = Modifier, radius: Dp = 6.dp) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Text-sized shimmer placeholder. Sizes itself to the bounding box of the [text] measured in the
|
||||
* typography preset selected by [style], plus the preset's vertical padding (top + bottom).
|
||||
* Text-line shimmer placeholder, sized and styled after the typography line described by [style].
|
||||
*
|
||||
* @param text Text used to determine the shimmer's size. Not drawn.
|
||||
* @param style Typography preset — drives both the measurement style and the vertical padding.
|
||||
* @param radius Corner radius of the rectangle.
|
||||
* [Figma](https://www.figma.com/design/AsnJ5CPHib4Qxw12gszjMS/%F0%9F%92%A0-DS-Components?node-id=3398-625&p=f&m=dev)
|
||||
*
|
||||
* @param style A [TangemTheme.typography3] style (e.g. `TangemTheme.typography3.body.medium`) the
|
||||
* placeholder is sized after. Unrecognized styles fall back to `body.medium`.
|
||||
* @param modifier Modifier applied to the shimmer's root.
|
||||
* @param textAlign Horizontal position of the block within the parent width.
|
||||
*/
|
||||
@Composable
|
||||
fun TextShimmer(text: String, style: TextShimmerStyle, radius: Dp, modifier: Modifier = Modifier) {
|
||||
val textStyle = style.toTextStyle()
|
||||
val measurer = rememberTextMeasurer()
|
||||
val density = LocalDensity.current
|
||||
val (widthDp, heightDp) = remember(text, textStyle, measurer, density) {
|
||||
val measured = measurer.measure(text = text, style = textStyle)
|
||||
with(density) { measured.size.width.toDp() to measured.size.height.toDp() }
|
||||
fun TangemShimmer(style: TextStyle, modifier: Modifier = Modifier, textAlign: TextAlign = TextAlign.Start) {
|
||||
val preset = TangemShimmer.TextPreset.forStyle(style)
|
||||
val lineHeightDp = with(LocalDensity.current) { style.lineHeight.toDp() }
|
||||
val alignment = when (textAlign) {
|
||||
TextAlign.Center -> Alignment.Center
|
||||
TextAlign.End -> Alignment.CenterEnd
|
||||
else -> Alignment.CenterStart
|
||||
}
|
||||
|
||||
RectangleShimmer(
|
||||
modifier = modifier.size(
|
||||
width = widthDp,
|
||||
height = heightDp + style.verticalPadding * 2,
|
||||
),
|
||||
radius = radius,
|
||||
)
|
||||
Box(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
contentAlignment = alignment,
|
||||
) {
|
||||
TangemShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(preset.widthFraction)
|
||||
.height(lineHeightDp)
|
||||
.padding(vertical = preset.verticalPadding),
|
||||
radius = preset.radius,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Public API namespace for [TangemShimmer]. */
|
||||
object TangemShimmer {
|
||||
|
||||
/** Default corner radius of the rectangle shimmer. */
|
||||
val DefaultRadius: Dp = 6.dp
|
||||
|
||||
/** Per-typography sizing for the [TangemShimmer] text overload. */
|
||||
internal enum class TextPreset(val widthFraction: Float, val verticalPadding: Dp, val radius: Dp) {
|
||||
Display(widthFraction = 0.5f, verticalPadding = 4.dp, radius = 12.dp),
|
||||
HeadingMedium(widthFraction = 0.7f, verticalPadding = 2.dp, radius = 8.dp),
|
||||
HeadingSmall(widthFraction = 0.6f, verticalPadding = 2.dp, radius = 16.dp),
|
||||
Body(widthFraction = 0.5f, verticalPadding = 2.dp, radius = 16.dp),
|
||||
Subheading(widthFraction = 0.4f, verticalPadding = 2.dp, radius = 16.dp),
|
||||
Caption(widthFraction = 0.3f, verticalPadding = 2.dp, radius = 16.dp),
|
||||
;
|
||||
|
||||
companion object {
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
fun forStyle(style: TextStyle): TextPreset {
|
||||
val typography = TangemTheme.typography3
|
||||
return when (style) {
|
||||
typography.display.medium -> Display
|
||||
typography.heading.medium -> HeadingMedium
|
||||
typography.heading.small -> HeadingSmall
|
||||
typography.subheading.medium -> Subheading
|
||||
typography.caption.medium -> Caption
|
||||
else -> Body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Typography preset for [TextShimmer]. Each preset maps to a [TangemTheme.typography3] style
|
||||
* and contributes additional [verticalPadding] applied to both top and bottom — the shimmer
|
||||
* block ends up `2 * verticalPadding` taller than the raw measured text.
|
||||
*/
|
||||
enum class TextShimmerStyle(val verticalPadding: Dp) {
|
||||
DISPLAY(verticalPadding = 4.dp),
|
||||
HEADING_MEDIUM(verticalPadding = 2.dp),
|
||||
HEADING_SMALL(verticalPadding = 2.dp),
|
||||
BODY(verticalPadding = 2.dp),
|
||||
SUBHEADING(verticalPadding = 2.dp),
|
||||
CAPTION(verticalPadding = 2.dp),
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun TextShimmerStyle.toTextStyle(): TextStyle = when (this) {
|
||||
TextShimmerStyle.DISPLAY -> TangemTheme.typography3.display.medium
|
||||
TextShimmerStyle.HEADING_MEDIUM -> TangemTheme.typography3.heading.medium
|
||||
TextShimmerStyle.HEADING_SMALL -> TangemTheme.typography3.heading.small
|
||||
TextShimmerStyle.BODY -> TangemTheme.typography3.body.medium
|
||||
TextShimmerStyle.SUBHEADING -> TangemTheme.typography3.subheading.medium
|
||||
TextShimmerStyle.CAPTION -> TangemTheme.typography3.caption.medium
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps [content] so every [RectangleShimmer] / [TextShimmer] inside reuses a single shimmer
|
||||
* animation driver. Without this provider each shimmer creates its own
|
||||
* [rememberInfiniteTransition] — that scales poorly in lists and lets sweeps drift out of phase.
|
||||
* Safe to nest; safe to omit (each shimmer falls back to its own driver).
|
||||
* Wraps [content] so every [TangemShimmer] inside shares a single, in-phase animation driver —
|
||||
* use it around lists of shimmers. Safe to nest; safe to omit.
|
||||
*/
|
||||
@Composable
|
||||
fun ProvideTangemShimmer(content: @Composable () -> Unit) {
|
||||
|
|
@ -198,24 +204,16 @@ private fun TangemShimmerPreview() {
|
|||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
RectangleShimmer(
|
||||
TangemShimmer(
|
||||
modifier = Modifier.size(width = 200.dp, height = 24.dp),
|
||||
radius = 6.dp,
|
||||
)
|
||||
RectangleShimmer(
|
||||
TangemShimmer(
|
||||
modifier = Modifier.size(width = 120.dp, height = 16.dp),
|
||||
radius = 4.dp,
|
||||
)
|
||||
TextShimmer(
|
||||
text = "Account balance",
|
||||
style = TextShimmerStyle.BODY,
|
||||
radius = 4.dp,
|
||||
)
|
||||
TextShimmer(
|
||||
text = "$12,345.67",
|
||||
style = TextShimmerStyle.HEADING_MEDIUM,
|
||||
radius = 6.dp,
|
||||
)
|
||||
TangemShimmer(style = TangemTheme.typography3.body.medium)
|
||||
TangemShimmer(style = TangemTheme.typography3.heading.medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.text.selection.TextSelectionColors
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.*
|
||||
import com.tangem.core.ui.components.haze.ProvideHaze
|
||||
import com.tangem.core.ui.ds2.shimmers.ProvideTangemShimmer
|
||||
import com.tangem.core.ui.res.generated.TangemTypography3
|
||||
import com.tangem.core.ui.res.generated.darkColors3
|
||||
import com.tangem.core.ui.res.generated.lightColors3
|
||||
|
|
@ -48,8 +49,10 @@ fun TangemThemeRedesign(content: @Composable () -> Unit) {
|
|||
CompositionLocalProvider(
|
||||
LocalTextSelectionColors provides TangemTextSelectionColors2,
|
||||
) {
|
||||
ProvideHaze {
|
||||
content()
|
||||
ProvideTangemShimmer {
|
||||
ProvideHaze {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTr
|
|||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle
|
||||
import com.tangem.core.ui.ds2.shimmers.TangemShimmer
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -105,11 +104,7 @@ private fun AmountBlock(state: TangemPayCardLimitSetupUM, modifier: Modifier = M
|
|||
color = TangemTheme.colors3.text.tertiary,
|
||||
)
|
||||
if (state.isInitialDataLoading) {
|
||||
TextShimmer(
|
||||
style = TextShimmerStyle.HEADING_MEDIUM,
|
||||
text = "$ 10000",
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
TangemShimmer(style = TangemTheme.typography3.heading.medium)
|
||||
} else {
|
||||
val colors = TangemAmountTextFieldColors.copy(
|
||||
textColor = TangemTheme.colors3.text.primary,
|
||||
|
|
|
|||
|
|
@ -28,9 +28,7 @@ import com.tangem.core.ui.ds.image.TangemIconUM
|
|||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.ds2.shimmers.RectangleShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle
|
||||
import com.tangem.core.ui.ds2.shimmers.TangemShimmer
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.*
|
||||
|
|
@ -180,7 +178,7 @@ private fun CurrentLimitBlockV2(state: TangemPayDailyLimitBlockState, modifier:
|
|||
)
|
||||
}
|
||||
TangemPayDailyLimitBlockState.Loading -> {
|
||||
RectangleShimmer(
|
||||
TangemShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x3)
|
||||
.layoutId(TangemRowLayoutId.TAIL)
|
||||
|
|
@ -273,12 +271,7 @@ private fun SubtitleLimit(state: TangemPayDailyLimitBlockState, modifier: Modifi
|
|||
)
|
||||
}
|
||||
TangemPayDailyLimitBlockState.Loading -> {
|
||||
TextShimmer(
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
modifier = modifier,
|
||||
style = TextShimmerStyle.BODY,
|
||||
text = "$50,000",
|
||||
)
|
||||
TangemShimmer(modifier = modifier, style = TangemTheme.typography3.body.medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ import com.tangem.core.ui.ds.message.TangemMessage
|
|||
import com.tangem.core.ui.ds.message.TangemMessageEffect
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle
|
||||
import com.tangem.core.ui.ds2.shimmers.TangemShimmer
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -343,11 +342,8 @@ private fun BalanceBlock(
|
|||
},
|
||||
) { animatedState ->
|
||||
when (animatedState) {
|
||||
is TangemPayDetailsBalanceBlockState.Loading -> TextShimmer(
|
||||
modifier = Modifier.size(width = 160.dp, height = 56.dp),
|
||||
text = "1234.00",
|
||||
style = TextShimmerStyle.HEADING_MEDIUM,
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
is TangemPayDetailsBalanceBlockState.Loading -> TangemShimmer(
|
||||
style = TangemTheme.typography3.heading.medium,
|
||||
)
|
||||
is TangemPayDetailsBalanceBlockState.Content -> Text(
|
||||
modifier = Modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ import com.tangem.core.ui.ds2.row.TangemRow
|
|||
import com.tangem.core.ui.ds2.row.TangemRowContentLead
|
||||
import com.tangem.core.ui.ds2.row.TangemRowText
|
||||
import com.tangem.core.ui.ds2.row.TangemRowTextRole
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle
|
||||
import com.tangem.core.ui.ds2.shimmers.TangemShimmer
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -174,11 +173,7 @@ private fun FeeInfoRow(titleRes: Int, value: String, showDivider: Boolean = fals
|
|||
},
|
||||
valueSlot = {
|
||||
if (value.isEmpty()) {
|
||||
TextShimmer(
|
||||
text = "$ 0.00",
|
||||
style = TextShimmerStyle.BODY,
|
||||
radius = 48.dp,
|
||||
)
|
||||
TangemShimmer(style = TangemTheme.typography3.body.medium)
|
||||
} else {
|
||||
TangemRowText(
|
||||
text = value,
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ import com.tangem.core.ui.ds2.row.TangemRow
|
|||
import com.tangem.core.ui.ds2.row.TangemRowText
|
||||
import com.tangem.core.ui.ds2.row.TangemRowTextRole
|
||||
import com.tangem.core.ui.ds2.row.TangemRowVerticalAlignment
|
||||
import com.tangem.core.ui.ds2.shimmers.RectangleShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle
|
||||
import com.tangem.core.ui.ds2.shimmers.TangemShimmer
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -175,12 +173,7 @@ private fun GroupTitleBlock(
|
|||
.fillMaxWidth()
|
||||
.padding(top = 6.dp, start = 16.dp),
|
||||
) {
|
||||
TextShimmer(
|
||||
modifier = Modifier.width(TangemTheme.dimens2.x10),
|
||||
text = state.title,
|
||||
style = TextShimmerStyle.SUBHEADING,
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
TangemShimmer(style = TangemTheme.typography3.subheading.medium)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
|
|
@ -224,7 +217,7 @@ private fun Icon(state: TangemPayTransactionState, modifier: Modifier = Modifier
|
|||
iconState = state.iconV2,
|
||||
modifier = modifier,
|
||||
)
|
||||
is TangemPayTransactionState.Loading -> RectangleShimmer(
|
||||
is TangemPayTransactionState.Loading -> TangemShimmer(
|
||||
modifier = modifier.size(TangemTheme.dimens2.x10),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
|
|
@ -266,12 +259,7 @@ private fun Title(state: TangemPayTransactionState, modifier: Modifier = Modifie
|
|||
TangemRowText(text = state.title.resolveReference(), role = TangemRowTextRole.Title)
|
||||
}
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
TextShimmer(
|
||||
modifier = modifier,
|
||||
text = "Transfer",
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
style = TextShimmerStyle.BODY,
|
||||
)
|
||||
TangemShimmer(modifier = modifier, style = TangemTheme.typography3.body.medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -283,12 +271,7 @@ private fun Subtitle(state: TangemPayTransactionState, modifier: Modifier = Modi
|
|||
TangemRowText(text = state.subtitle.resolveReference(), role = TangemRowTextRole.Subtitle)
|
||||
}
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
TextShimmer(
|
||||
modifier = modifier,
|
||||
text = "Transfer",
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
style = TextShimmerStyle.CAPTION,
|
||||
)
|
||||
TangemShimmer(modifier = modifier, style = TangemTheme.typography3.caption.medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -306,12 +289,7 @@ private fun Amount(state: TangemPayTransactionState, isBalanceHidden: Boolean, m
|
|||
)
|
||||
}
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
TextShimmer(
|
||||
modifier = modifier,
|
||||
text = "10000",
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
style = TextShimmerStyle.BODY,
|
||||
)
|
||||
TangemShimmer(modifier = modifier, style = TangemTheme.typography3.body.medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -323,12 +301,7 @@ private fun Timestamp(state: TangemPayTransactionState, modifier: Modifier = Mod
|
|||
TangemRowText(text = state.time, role = TangemRowTextRole.Subvalue)
|
||||
}
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
TextShimmer(
|
||||
modifier = modifier,
|
||||
text = "00:00",
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
style = TextShimmerStyle.CAPTION,
|
||||
)
|
||||
TangemShimmer(modifier = modifier, style = TangemTheme.typography3.caption.medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.tangem.core.ui.ds2.fade.TangemFade
|
|||
import com.tangem.core.ui.ds2.loader.TangemLoaderSize
|
||||
import com.tangem.core.ui.ds2.row.TangemRowContentLead
|
||||
import com.tangem.core.ui.ds2.row.TangemRowVerticalAlignment
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle
|
||||
import com.tangem.core.ui.ds2.topnavigation.TangemTopNavigation
|
||||
|
||||
internal sealed interface StoryBookPage
|
||||
|
|
@ -113,16 +112,35 @@ internal data class TangemLoaderStory(
|
|||
|
||||
@Immutable
|
||||
internal data class TangemShimmerStory(
|
||||
val textStyle: TextShimmerStyle,
|
||||
val textStyle: TextStyleOption,
|
||||
val textPosition: TextPositionOption,
|
||||
val radius: RadiusOption,
|
||||
val rectangleWidth: RectangleWidthOption,
|
||||
val rectangleHeight: RectangleHeightOption,
|
||||
val onTextStyleChange: (TextShimmerStyle) -> Unit,
|
||||
val onTextStyleChange: (TextStyleOption) -> Unit,
|
||||
val onTextPositionChange: (TextPositionOption) -> Unit,
|
||||
val onRadiusChange: (RadiusOption) -> Unit,
|
||||
val onRectangleWidthChange: (RectangleWidthOption) -> Unit,
|
||||
val onRectangleHeightChange: (RectangleHeightOption) -> Unit,
|
||||
) : DsStoryBookPage {
|
||||
|
||||
/** Selectable typography preset for the `TangemShimmer` text variant. */
|
||||
enum class TextStyleOption {
|
||||
DISPLAY,
|
||||
HEADING_MEDIUM,
|
||||
HEADING_SMALL,
|
||||
BODY,
|
||||
SUBHEADING,
|
||||
CAPTION,
|
||||
}
|
||||
|
||||
/** Horizontal position of the `TangemShimmer` text block within the parent width. */
|
||||
enum class TextPositionOption(val label: String) {
|
||||
START("Start"),
|
||||
CENTER("Center"),
|
||||
END("End"),
|
||||
}
|
||||
|
||||
/** Selectable corner radius (matches `borderRadius` tokens). */
|
||||
enum class RadiusOption(val label: String) {
|
||||
R4("4dp"),
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.page.ds.shimmer
|
||||
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemShimmerStory
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory
|
||||
|
||||
internal fun StateUpdater<TangemShimmerStory>.build(): TangemShimmerStory {
|
||||
return TangemShimmerStory(
|
||||
textStyle = TextShimmerStyle.BODY,
|
||||
textStyle = TangemShimmerStory.TextStyleOption.BODY,
|
||||
textPosition = TangemShimmerStory.TextPositionOption.START,
|
||||
radius = TangemShimmerStory.RadiusOption.R24,
|
||||
rectangleWidth = TangemShimmerStory.RectangleWidthOption.W240,
|
||||
rectangleHeight = TangemShimmerStory.RectangleHeightOption.H24,
|
||||
onTextStyleChange = { textStyle ->
|
||||
updateStory { it.copy(textStyle = textStyle) }
|
||||
},
|
||||
onTextPositionChange = { position ->
|
||||
updateStory { it.copy(textPosition = position) }
|
||||
},
|
||||
onRadiusChange = { radius ->
|
||||
updateStory { it.copy(radius = radius) }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.ds2.shimmers.RectangleShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.core.ui.ds2.shimmers.TangemShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemShimmerStory
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemShimmerStory.*
|
||||
|
|
@ -38,12 +38,20 @@ internal fun TangemShimmerStory(state: TangemShimmerStory, modifier: Modifier =
|
|||
) {
|
||||
ChipSection(label = "Text style") {
|
||||
ChipGrid(
|
||||
items = TextShimmerStyle.entries,
|
||||
items = TextStyleOption.entries,
|
||||
label = { it.chipLabel() },
|
||||
isSelected = { it == state.textStyle },
|
||||
onSelect = state.onTextStyleChange,
|
||||
)
|
||||
}
|
||||
ChipSection(label = "Text position") {
|
||||
ChipGrid(
|
||||
items = TextPositionOption.entries,
|
||||
label = { it.label },
|
||||
isSelected = { it == state.textPosition },
|
||||
onSelect = state.onTextPositionChange,
|
||||
)
|
||||
}
|
||||
ChipSection(label = "Radius") {
|
||||
ChipGrid(
|
||||
items = RadiusOption.entries,
|
||||
|
|
@ -89,18 +97,17 @@ private fun ComponentPreview(state: TangemShimmerStory) {
|
|||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
PreviewLabel(text = "RectangleShimmer")
|
||||
PreviewLabel(text = "TangemShimmer · rectangle")
|
||||
RectangleShimmerPreview(
|
||||
width = state.rectangleWidth,
|
||||
height = state.rectangleHeight,
|
||||
radius = radius,
|
||||
)
|
||||
|
||||
PreviewLabel(text = "TextShimmer · ${state.textStyle.chipLabel()}")
|
||||
TextShimmer(
|
||||
text = SAMPLE_TEXT,
|
||||
style = state.textStyle,
|
||||
radius = radius,
|
||||
PreviewLabel(text = "TangemShimmer · text · ${state.textStyle.chipLabel()}")
|
||||
TangemShimmer(
|
||||
style = state.textStyle.toTextStyle(),
|
||||
textAlign = state.textPosition.toTextAlign(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -113,7 +120,7 @@ private fun RectangleShimmerPreview(width: RectangleWidthOption, height: Rectang
|
|||
else -> Modifier.width(width.value())
|
||||
}.height(height.value())
|
||||
|
||||
RectangleShimmer(
|
||||
TangemShimmer(
|
||||
modifier = sizeModifier,
|
||||
radius = radius,
|
||||
)
|
||||
|
|
@ -194,13 +201,29 @@ private fun Chip(label: String, selected: Boolean, onClick: () -> Unit, modifier
|
|||
|
||||
// endregion
|
||||
|
||||
private fun TextShimmerStyle.chipLabel(): String = when (this) {
|
||||
TextShimmerStyle.DISPLAY -> "Display"
|
||||
TextShimmerStyle.HEADING_MEDIUM -> "Head.M"
|
||||
TextShimmerStyle.HEADING_SMALL -> "Head.S"
|
||||
TextShimmerStyle.BODY -> "Body"
|
||||
TextShimmerStyle.SUBHEADING -> "Sub.H"
|
||||
TextShimmerStyle.CAPTION -> "Caption"
|
||||
private fun TextStyleOption.chipLabel(): String = when (this) {
|
||||
TextStyleOption.DISPLAY -> "Display"
|
||||
TextStyleOption.HEADING_MEDIUM -> "Head.M"
|
||||
TextStyleOption.HEADING_SMALL -> "Head.S"
|
||||
TextStyleOption.BODY -> "Body"
|
||||
TextStyleOption.SUBHEADING -> "Sub.H"
|
||||
TextStyleOption.CAPTION -> "Caption"
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TextStyleOption.toTextStyle(): TextStyle = when (this) {
|
||||
TextStyleOption.DISPLAY -> TangemTheme.typography3.display.medium
|
||||
TextStyleOption.HEADING_MEDIUM -> TangemTheme.typography3.heading.medium
|
||||
TextStyleOption.HEADING_SMALL -> TangemTheme.typography3.heading.small
|
||||
TextStyleOption.BODY -> TangemTheme.typography3.body.medium
|
||||
TextStyleOption.SUBHEADING -> TangemTheme.typography3.subheading.medium
|
||||
TextStyleOption.CAPTION -> TangemTheme.typography3.caption.medium
|
||||
}
|
||||
|
||||
private fun TextPositionOption.toTextAlign(): TextAlign = when (this) {
|
||||
TextPositionOption.START -> TextAlign.Start
|
||||
TextPositionOption.CENTER -> TextAlign.Center
|
||||
TextPositionOption.END -> TextAlign.End
|
||||
}
|
||||
|
||||
private fun RadiusOption.value(): Dp = when (this) {
|
||||
|
|
@ -224,6 +247,4 @@ private fun RectangleHeightOption.value(): Dp = when (this) {
|
|||
RectangleHeightOption.H24 -> 24.dp
|
||||
RectangleHeightOption.H40 -> 40.dp
|
||||
RectangleHeightOption.H64 -> 64.dp
|
||||
}
|
||||
|
||||
private const val SAMPLE_TEXT = "Sample shimmer text"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue