Updated on 2026-08-14
This commit is contained in:
parent
427715888d
commit
db9ec70213
15 changed files with 115 additions and 328 deletions
|
|
@ -87,8 +87,8 @@ fun TangemBadge(
|
|||
.heightIn(min = sizeTokens.minHeight),
|
||||
onClick = onClick,
|
||||
color = colorTokens.backgroundColor,
|
||||
border = colorTokens.borderColor?.let { BorderStroke(TangemTheme.dimens3.borderWidth.sm, it) },
|
||||
shape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.full),
|
||||
border = colorTokens.borderColor?.let { BorderStroke(1.dp, it) },
|
||||
shape = RoundedCornerShape(999.dp),
|
||||
) {
|
||||
BadgeContent(
|
||||
iconStart = iconStart,
|
||||
|
|
@ -206,30 +206,29 @@ private data class BadgeSizeTokens(
|
|||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun TangemBadge.Size.tokens(): BadgeSizeTokens {
|
||||
val dimens = TangemTheme.dimens3
|
||||
val typography = TangemTheme.typography3
|
||||
return when (this) {
|
||||
TangemBadge.Size.X9 -> BadgeSizeTokens(
|
||||
minHeight = dimens.size.s450,
|
||||
containerHorizontalPadding = dimens.spacing.s100,
|
||||
containerVerticalPadding = dimens.spacing.s100,
|
||||
labelPadding = dimens.spacing.s050,
|
||||
minHeight = 36.dp,
|
||||
containerHorizontalPadding = 8.dp,
|
||||
containerVerticalPadding = 8.dp,
|
||||
labelPadding = 4.dp,
|
||||
iconSize = 20.dp,
|
||||
textStyle = typography.subheading.medium,
|
||||
)
|
||||
TangemBadge.Size.X6 -> BadgeSizeTokens(
|
||||
minHeight = dimens.size.s300,
|
||||
containerHorizontalPadding = dimens.spacing.s050,
|
||||
containerVerticalPadding = dimens.spacing.s050,
|
||||
labelPadding = dimens.spacing.s050,
|
||||
minHeight = 24.dp,
|
||||
containerHorizontalPadding = 4.dp,
|
||||
containerVerticalPadding = 4.dp,
|
||||
labelPadding = 4.dp,
|
||||
iconSize = 16.dp,
|
||||
textStyle = typography.caption.medium,
|
||||
)
|
||||
TangemBadge.Size.X4 -> BadgeSizeTokens(
|
||||
minHeight = dimens.size.s200,
|
||||
containerHorizontalPadding = dimens.spacing.s025,
|
||||
containerVerticalPadding = dimens.spacing.none,
|
||||
labelPadding = dimens.spacing.s025,
|
||||
minHeight = 16.dp,
|
||||
containerHorizontalPadding = 2.dp,
|
||||
containerVerticalPadding = 0.dp,
|
||||
labelPadding = 2.dp,
|
||||
iconSize = 12.dp,
|
||||
textStyle = typography.caption.medium,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ fun TangemButton(
|
|||
enabled = isEnabled,
|
||||
color = backgroundColor,
|
||||
border = resolveBorder(isFocused = isFocused, colorTokens = colorTokens, contentAlpha = contentAlpha),
|
||||
shape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.full),
|
||||
shape = RoundedCornerShape(999.dp),
|
||||
interactionSource = interactionSource,
|
||||
isMaterial = variant == TangemButton.Variant.Material,
|
||||
) {
|
||||
|
|
@ -129,12 +129,12 @@ fun TangemButton(
|
|||
@Composable
|
||||
private fun resolveBorder(isFocused: Boolean, colorTokens: ColorTokens, contentAlpha: Float): BorderStroke? = when {
|
||||
isFocused -> BorderStroke(
|
||||
width = TangemTheme.dimens3.borderWidth.md,
|
||||
width = 2.dp,
|
||||
// Focus ring is intentionally NOT scaled by contentAlpha — see TangemButton above.
|
||||
color = colorTokens.focusRingColor,
|
||||
)
|
||||
colorTokens.defaultBorderColor != null -> BorderStroke(
|
||||
width = TangemTheme.dimens3.borderWidth.sm,
|
||||
width = 1.dp,
|
||||
color = colorTokens.defaultBorderColor.scaleAlpha(contentAlpha),
|
||||
)
|
||||
else -> null
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ internal fun TangemButton.Variant.tokens(): ColorTokens {
|
|||
disabledTextColor = TangemTheme.colors3.text.primary,
|
||||
disabledIconTint = TangemTheme.colors3.icon.primary,
|
||||
focusRingColor = TangemTheme.colors3.interaction.focusRing.brand,
|
||||
disabledAlpha = TangemTheme.dimens3.opacity.disabled,
|
||||
disabledAlpha = 0.4f,
|
||||
)
|
||||
TangemButton.Variant.Material -> ColorTokens(
|
||||
// Background is the haze fill (FILL/MATERIAL) rendered by TangemSurface when isMaterial = true;
|
||||
|
|
@ -303,7 +303,7 @@ internal fun TangemButton.Variant.tokens(): ColorTokens {
|
|||
disabledTextColor = TangemTheme.colors3.text.staticDark.primary,
|
||||
disabledIconTint = TangemTheme.colors3.icon.staticDark,
|
||||
focusRingColor = TangemTheme.colors3.interaction.focusRing.default,
|
||||
disabledAlpha = TangemTheme.dimens3.opacity.disabled,
|
||||
disabledAlpha = 0.4f,
|
||||
)
|
||||
TangemButton.Variant.Outline -> ColorTokens(
|
||||
backgroundColor = Color.Transparent,
|
||||
|
|
@ -313,7 +313,7 @@ internal fun TangemButton.Variant.tokens(): ColorTokens {
|
|||
disabledTextColor = TangemTheme.colors3.text.primary,
|
||||
disabledIconTint = TangemTheme.colors3.icon.primary,
|
||||
focusRingColor = TangemTheme.colors3.interaction.focusRing.brand,
|
||||
disabledAlpha = TangemTheme.dimens3.opacity.disabled,
|
||||
disabledAlpha = 0.4f,
|
||||
defaultBorderColor = TangemTheme.colors3.border.secondary,
|
||||
)
|
||||
TangemButton.Variant.Ghost -> ColorTokens(
|
||||
|
|
@ -324,7 +324,7 @@ internal fun TangemButton.Variant.tokens(): ColorTokens {
|
|||
disabledTextColor = TangemTheme.colors3.text.primary,
|
||||
disabledIconTint = TangemTheme.colors3.icon.primary,
|
||||
focusRingColor = TangemTheme.colors3.interaction.focusRing.brand,
|
||||
disabledAlpha = TangemTheme.dimens3.opacity.disabled,
|
||||
disabledAlpha = 0.4f,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -343,69 +343,68 @@ internal data class SizeTokens(
|
|||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun TangemButton.Size.tokens(): SizeTokens {
|
||||
val dimens = TangemTheme.dimens3
|
||||
return when (this) {
|
||||
TangemButton.Size.X14 -> SizeTokens(
|
||||
minHeight = dimens.size.s700,
|
||||
minWidth = dimens.size.s1100,
|
||||
minSizeIconOnly = dimens.size.s700,
|
||||
textPadding = dimens.spacing.s100,
|
||||
containerHorizontalPadding = dimens.spacing.s200,
|
||||
containerVerticalPadding = dimens.spacing.s200,
|
||||
minHeight = 56.dp,
|
||||
minWidth = 88.dp,
|
||||
minSizeIconOnly = 56.dp,
|
||||
textPadding = 8.dp,
|
||||
containerHorizontalPadding = 16.dp,
|
||||
containerVerticalPadding = 16.dp,
|
||||
iconSize = 24.dp,
|
||||
)
|
||||
TangemButton.Size.X12 -> SizeTokens(
|
||||
minHeight = dimens.size.s600,
|
||||
minWidth = dimens.size.s1000,
|
||||
minSizeIconOnly = dimens.size.s600,
|
||||
textPadding = dimens.spacing.s100,
|
||||
containerHorizontalPadding = dimens.spacing.s150,
|
||||
containerVerticalPadding = dimens.spacing.s150,
|
||||
minHeight = 48.dp,
|
||||
minWidth = 80.dp,
|
||||
minSizeIconOnly = 48.dp,
|
||||
textPadding = 8.dp,
|
||||
containerHorizontalPadding = 12.dp,
|
||||
containerVerticalPadding = 12.dp,
|
||||
iconSize = 24.dp,
|
||||
)
|
||||
TangemButton.Size.X11 -> SizeTokens(
|
||||
minHeight = dimens.size.s550,
|
||||
minWidth = dimens.size.s900,
|
||||
minSizeIconOnly = dimens.size.s550,
|
||||
textPadding = dimens.spacing.s075,
|
||||
containerHorizontalPadding = dimens.spacing.s150,
|
||||
containerVerticalPadding = dimens.spacing.s150,
|
||||
minHeight = 44.dp,
|
||||
minWidth = 72.dp,
|
||||
minSizeIconOnly = 44.dp,
|
||||
textPadding = 6.dp,
|
||||
containerHorizontalPadding = 12.dp,
|
||||
containerVerticalPadding = 12.dp,
|
||||
iconSize = 20.dp,
|
||||
)
|
||||
TangemButton.Size.X10 -> SizeTokens(
|
||||
minHeight = dimens.size.s500,
|
||||
minWidth = dimens.size.s800,
|
||||
minSizeIconOnly = dimens.size.s500,
|
||||
textPadding = dimens.spacing.s075,
|
||||
containerHorizontalPadding = dimens.spacing.s125,
|
||||
containerVerticalPadding = dimens.spacing.s125,
|
||||
minHeight = 40.dp,
|
||||
minWidth = 64.dp,
|
||||
minSizeIconOnly = 40.dp,
|
||||
textPadding = 6.dp,
|
||||
containerHorizontalPadding = 10.dp,
|
||||
containerVerticalPadding = 10.dp,
|
||||
iconSize = 20.dp,
|
||||
)
|
||||
TangemButton.Size.X9 -> SizeTokens(
|
||||
minHeight = dimens.size.s450,
|
||||
minWidth = dimens.size.s700,
|
||||
minSizeIconOnly = dimens.size.s450,
|
||||
textPadding = dimens.spacing.s075,
|
||||
containerHorizontalPadding = dimens.spacing.s100,
|
||||
containerVerticalPadding = dimens.spacing.s100,
|
||||
minHeight = 36.dp,
|
||||
minWidth = 56.dp,
|
||||
minSizeIconOnly = 36.dp,
|
||||
textPadding = 6.dp,
|
||||
containerHorizontalPadding = 8.dp,
|
||||
containerVerticalPadding = 8.dp,
|
||||
iconSize = 20.dp,
|
||||
)
|
||||
TangemButton.Size.X8 -> SizeTokens(
|
||||
minHeight = dimens.size.s400,
|
||||
minWidth = dimens.size.s600,
|
||||
minSizeIconOnly = dimens.size.s400,
|
||||
textPadding = dimens.spacing.s075,
|
||||
containerHorizontalPadding = dimens.spacing.s075,
|
||||
containerVerticalPadding = dimens.spacing.s075,
|
||||
minHeight = 32.dp,
|
||||
minWidth = 48.dp,
|
||||
minSizeIconOnly = 32.dp,
|
||||
textPadding = 6.dp,
|
||||
containerHorizontalPadding = 6.dp,
|
||||
containerVerticalPadding = 6.dp,
|
||||
iconSize = 20.dp,
|
||||
)
|
||||
TangemButton.Size.X7 -> SizeTokens(
|
||||
minHeight = dimens.size.s350,
|
||||
minWidth = dimens.size.s500,
|
||||
minSizeIconOnly = dimens.size.s350,
|
||||
textPadding = dimens.spacing.s075,
|
||||
containerHorizontalPadding = dimens.spacing.s075,
|
||||
containerVerticalPadding = dimens.spacing.s050,
|
||||
minHeight = 28.dp,
|
||||
minWidth = 40.dp,
|
||||
minSizeIconOnly = 28.dp,
|
||||
textPadding = 6.dp,
|
||||
containerHorizontalPadding = 6.dp,
|
||||
containerVerticalPadding = 4.dp,
|
||||
iconSize = 16.dp,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,12 +139,12 @@ fun TangemRow(
|
|||
.conditionalCompose(divider) {
|
||||
bottomDivider(
|
||||
color = TangemTheme.colors3.border.secondary,
|
||||
width = TangemTheme.dimens3.borderWidth.sm,
|
||||
horizontalInset = TangemTheme.dimens3.spacing.s200,
|
||||
width = 1.dp,
|
||||
horizontalInset = 16.dp,
|
||||
)
|
||||
}
|
||||
.conditionalCompose(includeInnerPaddings) {
|
||||
padding(TangemTheme.dimens3.spacing.s200)
|
||||
padding(16.dp)
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
|
|
@ -164,7 +164,7 @@ fun TangemRow(
|
|||
SideSlot(slot = endSlot, position = SideSlotPosition.End)
|
||||
}
|
||||
if (extraBottomSlot != null) {
|
||||
SpacerH(TangemTheme.dimens3.spacing.s100)
|
||||
SpacerH(8.dp)
|
||||
extraBottomSlot()
|
||||
}
|
||||
}
|
||||
|
|
@ -202,7 +202,7 @@ private enum class SideSlotPosition { Start, End }
|
|||
@Composable
|
||||
private fun SideSlot(slot: (@Composable BoxScope.() -> Unit)?, position: SideSlotPosition) {
|
||||
if (slot == null) return
|
||||
val spacing = TangemTheme.dimens3.spacing.s150
|
||||
val spacing = 12.dp
|
||||
val padding = when (position) {
|
||||
SideSlotPosition.Start -> Modifier.padding(end = spacing)
|
||||
SideSlotPosition.End -> Modifier.padding(start = spacing)
|
||||
|
|
@ -297,7 +297,7 @@ private fun LabelColumnContent(
|
|||
alignment: Alignment.Horizontal,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens3.spacing.s025),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
horizontalAlignment = alignment,
|
||||
) {
|
||||
if (primary != null) LabelRow(alignment = alignment, content = primary)
|
||||
|
|
@ -309,7 +309,7 @@ private fun LabelColumnContent(
|
|||
private fun LabelRow(alignment: Alignment.Horizontal, content: @Composable RowScope.() -> Unit) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
space = TangemTheme.dimens3.spacing.s050,
|
||||
space = 4.dp,
|
||||
alignment = alignment,
|
||||
),
|
||||
content = content,
|
||||
|
|
@ -399,10 +399,10 @@ private fun rowTextAlign(role: TangemRowTextRole): TextAlign = when (role) {
|
|||
|
||||
@Composable
|
||||
private fun Modifier.focusBorder(): Modifier {
|
||||
val radius = TangemTheme.dimens3.borderRadius.b200
|
||||
val radius = 16.dp
|
||||
val shape = remember(radius) { RoundedCornerShape(radius) }
|
||||
return border(
|
||||
width = TangemTheme.dimens3.borderWidth.md,
|
||||
width = 2.dp,
|
||||
color = TangemTheme.colors3.interaction.focusRing.default,
|
||||
shape = shape,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ fun TangemSearch(
|
|||
TangemSurface(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.heightIn(min = TangemTheme.dimens3.size.s550),
|
||||
.heightIn(min = 44.dp),
|
||||
isMaterial = true,
|
||||
shape = CircleShape,
|
||||
onClick = focusRequester::requestFocus,
|
||||
|
|
@ -131,14 +131,14 @@ private fun SearchField(state: TangemSearch.State, focusRequester: FocusRequeste
|
|||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(
|
||||
start = TangemTheme.dimens3.spacing.s150,
|
||||
top = TangemTheme.dimens3.spacing.s150,
|
||||
bottom = TangemTheme.dimens3.spacing.s150,
|
||||
start = 12.dp,
|
||||
top = 12.dp,
|
||||
bottom = 12.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = TangemTheme.dimens3.spacing.s100),
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
imageVector = Icons.ic_search_20,
|
||||
tint = TangemTheme.colors3.icon.primary,
|
||||
contentDescription = null,
|
||||
|
|
@ -203,7 +203,7 @@ private fun QueryTextField(state: TangemSearch.State, focusRequester: FocusReque
|
|||
Box {
|
||||
if (state.query.isEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(end = TangemTheme.dimens3.spacing.s250),
|
||||
modifier = Modifier.padding(end = 20.dp),
|
||||
text = placeholder,
|
||||
style = sharedTextStyle,
|
||||
color = TangemTheme.colors3.text.secondary,
|
||||
|
|
@ -220,7 +220,7 @@ private fun QueryTextField(state: TangemSearch.State, focusRequester: FocusReque
|
|||
@Composable
|
||||
private fun ClearButton(onClick: () -> Unit) {
|
||||
TangemButton(
|
||||
modifier = Modifier.padding(end = TangemTheme.dimens3.spacing.s050),
|
||||
modifier = Modifier.padding(end = 4.dp),
|
||||
size = TangemButton.Size.X9,
|
||||
variant = TangemButton.Variant.Ghost,
|
||||
iconStart = TangemIconUM.Icon(Icons.ic_cross_circle_20_filled),
|
||||
|
|
@ -233,7 +233,7 @@ private fun CloseButton(onClick: () -> Unit) {
|
|||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
TangemButton(
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens3.spacing.s100),
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
size = TangemButton.Size.X11,
|
||||
variant = TangemButton.Variant.Material,
|
||||
iconStart = TangemIconUM.Icon(Icons.ic_cross_20),
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ fun TangemSurface(
|
|||
color: Color = TangemTheme.colors3.bg.primary,
|
||||
isMaterial: Boolean = false,
|
||||
border: BorderStroke? = null,
|
||||
shape: Shape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.b200),
|
||||
shape: Shape = RoundedCornerShape(16.dp),
|
||||
onClick: (() -> Unit)? = null,
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource? = null,
|
||||
|
|
@ -123,7 +123,7 @@ private fun Modifier.materialShadow(shape: Shape): Modifier = softLayerShadow(
|
|||
/** Diagonal gradient stroke that wraps the material variant. */
|
||||
@Composable
|
||||
private fun Modifier.materialBorder(shape: Shape): Modifier = border(
|
||||
width = TangemTheme.dimens3.borderWidth.sm,
|
||||
width = 1.dp,
|
||||
brush = materialBorderBrush(),
|
||||
shape = shape,
|
||||
)
|
||||
|
|
@ -145,7 +145,7 @@ private fun Modifier.materialFill(): Modifier {
|
|||
val hazed = hazeEffectTangem(
|
||||
style = HazeStyle(
|
||||
backgroundColor = TangemTheme.colors3.material.fill.blur,
|
||||
blurRadius = TangemTheme.dimens3.blur.Button,
|
||||
blurRadius = 32.dp,
|
||||
tints = emptyList(),
|
||||
),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -84,16 +84,16 @@ fun TangemTopNavigation(
|
|||
blur = blurBackground,
|
||||
)
|
||||
|
||||
val groupSpacing = TangemTheme.dimens3.spacing.s100
|
||||
val groupSpacing = 8.dp
|
||||
Layout(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(windowInsets)
|
||||
.padding(
|
||||
top = TangemTheme.dimens3.spacing.s100,
|
||||
bottom = TangemTheme.dimens3.spacing.s200,
|
||||
start = TangemTheme.dimens3.spacing.s200,
|
||||
end = TangemTheme.dimens3.spacing.s200,
|
||||
top = 8.dp,
|
||||
bottom = 16.dp,
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
),
|
||||
content = {
|
||||
// Each optional slot caches its last non-null content so the spring exit transition
|
||||
|
|
@ -110,7 +110,7 @@ fun TangemTopNavigation(
|
|||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens3.spacing.s150)
|
||||
.padding(horizontal = 12.dp)
|
||||
.layoutId(SlotId.Content),
|
||||
horizontalAlignment = when (contentAlign) {
|
||||
TangemTopNavigation.ContentAlign.Start -> Alignment.Start
|
||||
|
|
@ -130,7 +130,7 @@ fun TangemTopNavigation(
|
|||
displayedGroup?.let { group ->
|
||||
TangemSurface(isMaterial = true, shape = CircleShape) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens3.spacing.s050),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
content = group,
|
||||
)
|
||||
}
|
||||
|
|
@ -307,7 +307,7 @@ private fun ColumnScope.TitleSubtitle(title: TextReference, subtitle: TextRefere
|
|||
) {
|
||||
displayedSubtitle?.let { text ->
|
||||
Column {
|
||||
Spacer(Modifier.height(TangemTheme.dimens3.spacing.s025))
|
||||
Spacer(Modifier.height(2.dp))
|
||||
TangemNavigationText(text = text, role = TangemNavigationText.Role.Subtitle)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import com.tangem.core.ui.haptic.HapticManager
|
|||
import com.tangem.core.ui.haptic.VibratorHapticManager
|
||||
import com.tangem.core.ui.message.EventMessageHandler
|
||||
import com.tangem.core.ui.res.generated.TangemColors3
|
||||
import com.tangem.core.ui.res.generated.TangemDimens3
|
||||
import com.tangem.core.ui.res.generated.TangemTypography3
|
||||
import com.tangem.core.ui.res.generated.lightColors3
|
||||
import com.tangem.core.ui.windowsize.WindowSize
|
||||
|
|
@ -201,11 +200,6 @@ object TangemTheme {
|
|||
@ReadOnlyComposable
|
||||
get() = LocalTangemDimens2.current
|
||||
|
||||
val dimens3: TangemDimens3
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalTangemDimens3.current
|
||||
|
||||
val shapes: TangemShapes
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
|
|
@ -406,10 +400,6 @@ private val LocalTangemDimens2 = staticCompositionLocalOf {
|
|||
TangemDimens2()
|
||||
}
|
||||
|
||||
internal val LocalTangemDimens3 = staticCompositionLocalOf {
|
||||
TangemDimens3()
|
||||
}
|
||||
|
||||
internal val LocalTangemTypography3 = staticCompositionLocalOf {
|
||||
TangemTypography3(InterFamily)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ 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.res.generated.TangemDimens3
|
||||
import com.tangem.core.ui.res.generated.TangemTypography3
|
||||
import com.tangem.core.ui.res.generated.darkColors3
|
||||
import com.tangem.core.ui.res.generated.lightColors3
|
||||
|
|
@ -29,7 +28,6 @@ fun TangemThemeRedesign(content: @Composable () -> Unit) {
|
|||
|
||||
val rootBackgroundColor = rememberedColors.background.secondary
|
||||
|
||||
val tangemDimens3 = remember { TangemDimens3() }
|
||||
val tangemTypography3 = remember { TangemTypography3(InterFamily) }
|
||||
val tangemTypography2 = remember { TangemTypography2(InterFamily) }
|
||||
val tangemTypography = remember { TangemTypography(InterFamily, useMediumForRegular = true) }
|
||||
|
|
@ -42,7 +40,6 @@ fun TangemThemeRedesign(content: @Composable () -> Unit) {
|
|||
LocalTangemColors provides rememberedColors,
|
||||
LocalTangemColors2 provides if (LocalIsInDarkTheme.current) darkThemeColors2() else lightThemeColors2(),
|
||||
LocalTangemColors3 provides rememberedColors3,
|
||||
LocalTangemDimens3 provides tangemDimens3,
|
||||
LocalTangemTypography3 provides tangemTypography3,
|
||||
LocalTangemTypography2 provides tangemTypography2,
|
||||
LocalTangemTypography provides tangemTypography,
|
||||
|
|
|
|||
|
|
@ -1,110 +0,0 @@
|
|||
@file:Suppress("all")
|
||||
|
||||
package com.tangem.core.ui.res.generated
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* Auto-generated from design tokens. Do not edit manually.
|
||||
*/
|
||||
@Stable
|
||||
class TangemDimens3 internal constructor(
|
||||
val opacity: Opacity = Opacity(),
|
||||
val blur: Blur = Blur(),
|
||||
val borderRadius: BorderRadius = BorderRadius(),
|
||||
val borderWidth: BorderWidth = BorderWidth(),
|
||||
val size: Size = Size(),
|
||||
val spacing: Spacing = Spacing(),
|
||||
) {
|
||||
@Stable
|
||||
class Opacity internal constructor(
|
||||
val disabled: Float = 0.4f,
|
||||
)
|
||||
|
||||
@Stable
|
||||
class Blur internal constructor(
|
||||
val card: Dp = 48.dp,
|
||||
val Button: Dp = 32.dp,
|
||||
val Fade: Dp = 8.dp,
|
||||
val None: Dp = 0.dp,
|
||||
)
|
||||
|
||||
@Stable
|
||||
class BorderRadius internal constructor(
|
||||
val b100: Dp = 8.dp,
|
||||
val b150: Dp = 12.dp,
|
||||
val b200: Dp = 16.dp,
|
||||
val b250: Dp = 20.dp,
|
||||
val b300: Dp = 24.dp,
|
||||
val b400: Dp = 32.dp,
|
||||
val none: Dp = 0.dp,
|
||||
val b075: Dp = 6.dp,
|
||||
val b050: Dp = 4.dp,
|
||||
val full: Dp = 999.dp,
|
||||
)
|
||||
|
||||
@Stable
|
||||
class BorderWidth internal constructor(
|
||||
val none: Dp = 0.dp,
|
||||
val xs: Dp = 0.5.dp,
|
||||
val sm: Dp = 1.dp,
|
||||
val md: Dp = 2.dp,
|
||||
val lg: Dp = 4.dp,
|
||||
)
|
||||
|
||||
@Stable
|
||||
class Size internal constructor(
|
||||
val s100: Dp = 8.dp,
|
||||
val s125: Dp = 10.dp,
|
||||
val s150: Dp = 12.dp,
|
||||
val s200: Dp = 16.dp,
|
||||
val s250: Dp = 20.dp,
|
||||
val s300: Dp = 24.dp,
|
||||
val s350: Dp = 28.dp,
|
||||
val s400: Dp = 32.dp,
|
||||
val s450: Dp = 36.dp,
|
||||
val s500: Dp = 40.dp,
|
||||
val s550: Dp = 44.dp,
|
||||
val s600: Dp = 48.dp,
|
||||
val s700: Dp = 56.dp,
|
||||
val s800: Dp = 64.dp,
|
||||
val s900: Dp = 72.dp,
|
||||
val s1000: Dp = 80.dp,
|
||||
val s1100: Dp = 88.dp,
|
||||
val s1200: Dp = 96.dp,
|
||||
val s025: Dp = 2.dp,
|
||||
val s050: Dp = 4.dp,
|
||||
val card: Card = Card(),
|
||||
) {
|
||||
@Stable
|
||||
class Card internal constructor(
|
||||
val sm: Dp = 128.dp,
|
||||
)
|
||||
}
|
||||
|
||||
@Stable
|
||||
class Spacing internal constructor(
|
||||
val s100: Dp = 8.dp,
|
||||
val s125: Dp = 10.dp,
|
||||
val s150: Dp = 12.dp,
|
||||
val s200: Dp = 16.dp,
|
||||
val s250: Dp = 20.dp,
|
||||
val s300: Dp = 24.dp,
|
||||
val s350: Dp = 28.dp,
|
||||
val s400: Dp = 32.dp,
|
||||
val s450: Dp = 36.dp,
|
||||
val s500: Dp = 40.dp,
|
||||
val s550: Dp = 44.dp,
|
||||
val s600: Dp = 48.dp,
|
||||
val s700: Dp = 56.dp,
|
||||
val s800: Dp = 64.dp,
|
||||
val s900: Dp = 72.dp,
|
||||
val s1000: Dp = 80.dp,
|
||||
val s025: Dp = 2.dp,
|
||||
val s050: Dp = 4.dp,
|
||||
val s075: Dp = 6.dp,
|
||||
val none: Dp = 0.dp,
|
||||
)
|
||||
}
|
||||
|
|
@ -4,7 +4,18 @@ Generates Kotlin (Jetpack Compose) source files from design tokens defined in th
|
|||
|
||||
## Updating tokens
|
||||
|
||||
1. Update the `ds-tokens` submodule to the latest commit:
|
||||
> **Note:** You only need `git submodule update --remote` when you want to pull **new** design tokens
|
||||
> from the remote `ds-tokens` repository. If you're just regenerating Kotlin from the tokens already
|
||||
> checked out (e.g. changing the generation script), **skip step 1** — don't run it without the need,
|
||||
> as it moves the submodule pointer to the latest remote commit and pulls in unrelated token changes.
|
||||
>
|
||||
> For all other cases (a fresh checkout, or making sure the submodule is at the pinned commit), use:
|
||||
> ```bash
|
||||
> git submodule update --init --recursive
|
||||
> ```
|
||||
> This checks out the submodule at the commit already recorded in the repo, without pulling anything new.
|
||||
|
||||
1. *(Only if you need newer tokens)* Update the `ds-tokens` submodule to the latest commit:
|
||||
```bash
|
||||
git submodule update --remote core/ui/ds-tokens
|
||||
```
|
||||
|
|
@ -12,7 +23,7 @@ Generates Kotlin (Jetpack Compose) source files from design tokens defined in th
|
|||
```bash
|
||||
cd core/ui/token-gen && npm run build
|
||||
```
|
||||
3. Commit both the submodule pointer and generated files.
|
||||
3. Commit the generated files (and the submodule pointer too, only if you ran step 1).
|
||||
|
||||
## How it works
|
||||
|
||||
|
|
|
|||
|
|
@ -202,46 +202,6 @@ function renderTree(node, indent = 1) {
|
|||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a @Stable class tree for dimension tokens.
|
||||
* Each node with children becomes a nested @Stable class.
|
||||
* Props carry { default, type } values.
|
||||
*/
|
||||
function renderStableDimenClass(className, node, indent) {
|
||||
const pad = ' '.repeat(indent);
|
||||
const pad1 = ' '.repeat(indent + 1);
|
||||
const lines = [];
|
||||
|
||||
lines.push(`${pad}@Stable`);
|
||||
lines.push(`${pad}class ${className} internal constructor(`);
|
||||
|
||||
for (const { name, value } of node.props) {
|
||||
lines.push(`${pad1}val ${kotlinSafe(name)}: ${value.type} = ${value.default},`);
|
||||
}
|
||||
for (const [childName, childNode] of node.children) {
|
||||
const typeName = capitalize(childName);
|
||||
const propName = kotlinSafe(childName.charAt(0).toLowerCase() + childName.slice(1));
|
||||
lines.push(`${pad1}val ${propName}: ${typeName} = ${typeName}(),`);
|
||||
}
|
||||
|
||||
if (node.children.size === 0) {
|
||||
lines.push(`${pad})`);
|
||||
} else {
|
||||
lines.push(`${pad}) {`);
|
||||
|
||||
let first = true;
|
||||
for (const [childName, childNode] of node.children) {
|
||||
if (!first) lines.push('');
|
||||
first = false;
|
||||
lines.push(...renderStableDimenClass(capitalize(childName), childNode, indent + 1));
|
||||
}
|
||||
|
||||
lines.push(`${pad}}`);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalize the first letter of a string.
|
||||
*/
|
||||
|
|
@ -518,61 +478,6 @@ StyleDictionary.registerFormat({
|
|||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Kotlin format for TangemDimens3 — structured dimension tokens as @Immutable data class.
|
||||
* Generates nested @Immutable data classes from codeSyntax.Android paths (prefix: TangemTheme.dimens3.).
|
||||
* Includes spacing, size, borderRadius, borderWidth, blur, and semantic opacity tokens.
|
||||
*/
|
||||
StyleDictionary.registerFormat({
|
||||
name: 'kotlin/compose-dimens3',
|
||||
format: ({ dictionary }) => {
|
||||
const prefix = 'TangemTheme.dimens3.';
|
||||
|
||||
const entries = [];
|
||||
for (const token of dictionary.allTokens) {
|
||||
const ext = token.$extensions?.['com.figma.codeSyntax'];
|
||||
if (!ext?.Android?.startsWith(prefix)) continue;
|
||||
|
||||
const codePath = ext.Android.slice(prefix.length);
|
||||
const segPath = codePath.split('.');
|
||||
|
||||
const raw = parseFloat(token.$value);
|
||||
const tp = token.path.join('.');
|
||||
if (isNaN(raw)) throw new Error(`Non-numeric value for ${tp}: "${token.$value}"`);
|
||||
|
||||
// Opacity tokens → Float, all others → Dp
|
||||
const isOpacity = token.$type === 'opacity';
|
||||
const value = isOpacity ? `${raw}f` : `${raw}.dp`;
|
||||
const type = isOpacity ? 'Float' : 'Dp';
|
||||
|
||||
entries.push({ path: segPath, value, type });
|
||||
}
|
||||
|
||||
const tree = buildPropertyTree(entries.map(e => ({
|
||||
path: e.path,
|
||||
value: { default: e.value, type: e.type },
|
||||
})));
|
||||
|
||||
const classLines = renderStableDimenClass('TangemDimens3', tree, 0);
|
||||
|
||||
return [
|
||||
FILE_SUPPRESS,
|
||||
'',
|
||||
`package ${PACKAGE}`,
|
||||
'',
|
||||
'import androidx.compose.runtime.Stable',
|
||||
'import androidx.compose.ui.unit.Dp',
|
||||
'import androidx.compose.ui.unit.dp',
|
||||
'',
|
||||
'/**',
|
||||
' * Auto-generated from design tokens. Do not edit manually.',
|
||||
' */',
|
||||
...classLines,
|
||||
'',
|
||||
].join('\n');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Kotlin format for TangemTypography3 — @Stable class with nested categories.
|
||||
* Generates a class taking FontFamily, with nested classes for each typography category
|
||||
|
|
@ -849,8 +754,8 @@ const paletteSd = new StyleDictionary({
|
|||
await paletteSd.buildAllPlatforms();
|
||||
console.log(' ✓ TangemColorPalette.kt');
|
||||
|
||||
// Build theme-independent tokens (dimensions, typography)
|
||||
console.log('\nBuilding dimension and typography tokens...');
|
||||
// Build theme-independent tokens (typography)
|
||||
console.log('\nBuilding typography tokens...');
|
||||
|
||||
const sd = new StyleDictionary({
|
||||
source: sharedBuildSets.map(s => path.join(tokensDir, `${s}.json`)),
|
||||
|
|
@ -862,10 +767,6 @@ const sd = new StyleDictionary({
|
|||
transforms: composePlatformTransforms,
|
||||
buildPath: outputDir + '/',
|
||||
files: [
|
||||
{
|
||||
destination: 'TangemDimens3.kt',
|
||||
format: 'kotlin/compose-dimens3',
|
||||
},
|
||||
{
|
||||
destination: 'TangemTypography3.kt',
|
||||
format: 'kotlin/compose-typography3',
|
||||
|
|
@ -877,7 +778,6 @@ const sd = new StyleDictionary({
|
|||
});
|
||||
|
||||
await sd.buildAllPlatforms();
|
||||
console.log(' ✓ TangemDimens3.kt');
|
||||
console.log(' ✓ TangemTypography3.kt');
|
||||
|
||||
// ── Write source hash ─────────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.compose.ui.tooling.preview.Devices
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
|
|
@ -194,7 +195,7 @@ internal fun TransactionLabel(label: TransactionLabelUM, modifier: Modifier = Mo
|
|||
modifier = modifier
|
||||
.background(
|
||||
color = backgroundColor,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.b250),
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens2.x3,
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ internal fun TangemPayAddCardView(onClick: () -> Unit, modifier: Modifier = Modi
|
|||
height = TangemTheme.dimens2.x10,
|
||||
width = TangemTheme.dimens2.x14,
|
||||
)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens3.borderRadius.b075))
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(TangemTheme.colors3.bg.opaque.primary)
|
||||
.clickableSingle(onClick = onClick),
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
@ -132,7 +132,7 @@ private fun CardBackground(
|
|||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens3.borderRadius.b075))
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.drawBehind {
|
||||
drawRect(bgColor)
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ private fun CardBackground(
|
|||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors3.border.primary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.b075),
|
||||
shape = RoundedCornerShape(6.dp),
|
||||
)
|
||||
.clickableSingle(onClick = onClick),
|
||||
content = content,
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ private fun ComponentPreview(state: TangemShimmerStory) {
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens3.borderRadius.b200))
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors3.bg.secondary)
|
||||
.padding(vertical = 24.dp, horizontal = 16.dp),
|
||||
) {
|
||||
|
|
@ -145,7 +145,7 @@ private fun ChipSection(label: String, content: @Composable () -> Unit) {
|
|||
|
||||
@Composable
|
||||
private fun <T> ChipGrid(items: List<T>, label: (T) -> String, isSelected: (T) -> Boolean, onSelect: (T) -> Unit) {
|
||||
val shape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.full)
|
||||
val shape = RoundedCornerShape(999.dp)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -153,7 +153,7 @@ private fun <T> ChipGrid(items: List<T>, label: (T) -> String, isSelected: (T) -
|
|||
.clip(shape)
|
||||
.background(TangemTheme.colors3.bg.opaque.primary)
|
||||
.border(
|
||||
width = TangemTheme.dimens3.borderWidth.sm,
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors3.border.primary,
|
||||
shape = shape,
|
||||
)
|
||||
|
|
@ -173,7 +173,7 @@ private fun <T> ChipGrid(items: List<T>, label: (T) -> String, isSelected: (T) -
|
|||
|
||||
@Composable
|
||||
private fun Chip(label: String, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val chipShape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.full)
|
||||
val chipShape = RoundedCornerShape(999.dp)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue