Updated on 2026-08-14
This commit is contained in:
commit
cebe0dcdf1
563 changed files with 28013 additions and 2788 deletions
|
|
@ -109,51 +109,53 @@ tasks.named("preBuild") {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
/** Project - Domain */
|
||||
implementation(projects.domain.appTheme.models)
|
||||
implementation(projects.domain.express.models)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
|
||||
/** Project - Core */
|
||||
implementation(projects.core.res)
|
||||
implementation(projects.core.utils)
|
||||
api(projects.core.decompose)
|
||||
implementation(projects.core.error)
|
||||
// region DI
|
||||
implementation(deps.hilt.android)
|
||||
// endregion
|
||||
|
||||
/** AndroidX libraries */
|
||||
implementation(deps.androidx.fragment.ktx)
|
||||
// region Kotlin
|
||||
api(deps.kotlin.coroutines)
|
||||
api(deps.kotlin.immutable.collections)
|
||||
api(deps.kotlin.serialization.core)
|
||||
// endregion
|
||||
|
||||
// region Compose
|
||||
implementation(deps.compose.accompanist.permission)
|
||||
api(deps.compose.accompanist.systemUiController)
|
||||
api(deps.compose.coil)
|
||||
implementation(deps.compose.constraintLayout)
|
||||
api(deps.compose.foundation)
|
||||
api(deps.compose.material3)
|
||||
api(deps.compose.paging)
|
||||
api(deps.compose.reorderable)
|
||||
api(deps.compose.shimmer)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
implementation(deps.compose.ui.utils)
|
||||
// endregion
|
||||
|
||||
// region AndroidX
|
||||
api(deps.androidx.activity)
|
||||
implementation(deps.androidx.activity.compose)
|
||||
implementation(deps.androidx.annotation)
|
||||
implementation(deps.androidx.appCompat)
|
||||
implementation(deps.androidx.core)
|
||||
implementation(deps.androidx.core.ktx)
|
||||
implementation(deps.androidx.paging.runtime)
|
||||
implementation(deps.lifecycle.runtime.ktx)
|
||||
implementation(deps.androidx.palette)
|
||||
api(deps.androidx.palette)
|
||||
implementation(deps.androidx.savedState)
|
||||
implementation(deps.androidx.windowManager) {
|
||||
exclude(
|
||||
deps.kotlin.coroutines.android.get().module.group,
|
||||
deps.kotlin.coroutines.android.get().module.name
|
||||
deps.kotlin.coroutines.android.get().module.name,
|
||||
)
|
||||
}
|
||||
api(deps.lifecycle.compose)
|
||||
api(deps.lifecycle.runtime.ktx)
|
||||
// endregion
|
||||
|
||||
/** Compose */
|
||||
implementation(deps.compose.constraintLayout)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.paging)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
implementation(deps.compose.ui.utils)
|
||||
implementation(deps.compose.coil)
|
||||
implementation(deps.compose.navigation)
|
||||
implementation(deps.compose.navigation.hilt)
|
||||
api(deps.compose.reorderable)
|
||||
|
||||
/** Other libraries */
|
||||
implementation(deps.compose.accompanist.systemUiController)
|
||||
implementation(deps.compose.accompanist.permission)
|
||||
implementation(deps.material)
|
||||
implementation(deps.compose.shimmer)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.zxing.qrCore)
|
||||
api(deps.jodatime)
|
||||
implementation(deps.markdown)
|
||||
// region Other libraries
|
||||
api(deps.arrow.core)
|
||||
api(deps.haze) {
|
||||
exclude(module = "activity-compose")
|
||||
exclude(module = "activity")
|
||||
|
|
@ -164,9 +166,28 @@ dependencies {
|
|||
exclude(module = "activity")
|
||||
exclude(module = "activity-ktx")
|
||||
}
|
||||
api(deps.jodatime)
|
||||
api(deps.markdown)
|
||||
implementation(deps.material)
|
||||
implementation(deps.zxing.qrCore)
|
||||
// endregion
|
||||
|
||||
/** Tests */
|
||||
// region Core modules
|
||||
api(projects.core.decompose)
|
||||
api(projects.core.error)
|
||||
implementation(projects.core.res)
|
||||
implementation(projects.core.utils)
|
||||
// endregion
|
||||
|
||||
// region Domain models
|
||||
api(projects.domain.appTheme.models)
|
||||
api(projects.domain.express.models)
|
||||
api(projects.domain.models)
|
||||
// endregion
|
||||
|
||||
// region Tests
|
||||
testImplementation(deps.test.junit5)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(deps.test.junit5)
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -3,19 +3,22 @@ package com.tangem.core.ui.ds.button.action
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.semantics.disabled
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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.Constraints
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.ds.button.SecondaryTangemButton
|
||||
|
|
@ -32,6 +35,8 @@ import com.tangem.core.ui.test.BaseActionButtonsBlockTestTags
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
private val ACTION_BUTTONS_SPACING = 10.dp
|
||||
|
||||
/**
|
||||
* Action buttons row
|
||||
*
|
||||
|
|
@ -42,42 +47,73 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
*/
|
||||
@Composable
|
||||
fun ActionButtons(buttons: ImmutableList<TangemButtonUM>, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
val spacingPx = with(LocalDensity.current) { ACTION_BUTTONS_SPACING.roundToPx() }
|
||||
|
||||
Layout(
|
||||
modifier = modifier,
|
||||
) {
|
||||
buttons.forEachIndexed { index, button ->
|
||||
key(button.text to index) {
|
||||
val textColor = if (button.isEnabled) {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors2.text.status.disabled
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens2.x2_5)
|
||||
.testTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
|
||||
.semantics { if (!button.isEnabled) disabled() },
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SecondaryTangemButton(
|
||||
tangemIconUM = button.tangemIconUM,
|
||||
onClick = button.onClick,
|
||||
isEnabled = button.isEnabled,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
onLongClick = button.onLongClick,
|
||||
)
|
||||
Text(
|
||||
text = button.text.orEmpty().resolveReference(),
|
||||
style = TangemTheme.typography2.subheadlineMedium14,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
)
|
||||
content = {
|
||||
buttons.forEachIndexed { index, button ->
|
||||
key(button.text to index) {
|
||||
ActionButton(button = button)
|
||||
}
|
||||
}
|
||||
},
|
||||
) { measurables, constraints ->
|
||||
if (measurables.isEmpty()) {
|
||||
return@Layout layout(constraints.minWidth, constraints.minHeight) {}
|
||||
}
|
||||
val cellWidth = measurables.maxOf { it.maxIntrinsicWidth(constraints.maxHeight) }
|
||||
val cellConstraints = Constraints(
|
||||
minWidth = cellWidth,
|
||||
maxWidth = cellWidth,
|
||||
minHeight = 0,
|
||||
maxHeight = constraints.maxHeight,
|
||||
)
|
||||
val placeables = measurables.map { it.measure(cellConstraints) }
|
||||
|
||||
val contentWidth = cellWidth * placeables.size + spacingPx * (placeables.size - 1)
|
||||
val width = if (constraints.hasBoundedWidth) maxOf(constraints.maxWidth, contentWidth) else contentWidth
|
||||
val height = placeables.maxOf { it.height }
|
||||
|
||||
layout(width, height) {
|
||||
var x = (width - contentWidth) / 2
|
||||
placeables.forEach { placeable ->
|
||||
placeable.place(x = x, y = (height - placeable.height) / 2)
|
||||
x += cellWidth + spacingPx
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActionButton(button: TangemButtonUM, modifier: Modifier = Modifier) {
|
||||
val textColor = if (button.isEnabled) {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors2.text.status.disabled
|
||||
}
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens2.x2_5)
|
||||
.testTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
|
||||
.semantics { if (!button.isEnabled) disabled() },
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SecondaryTangemButton(
|
||||
tangemIconUM = button.tangemIconUM,
|
||||
onClick = button.onClick,
|
||||
isEnabled = button.isEnabled,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
onLongClick = button.onLongClick,
|
||||
)
|
||||
Text(
|
||||
text = button.text.orEmpty().resolveReference(),
|
||||
style = TangemTheme.typography2.subheadlineMedium14,
|
||||
color = textColor,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.BiasAlignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
|
|
@ -201,6 +200,7 @@ private fun DecorationBox(
|
|||
Field(
|
||||
state = state,
|
||||
innerTextField = innerTextField,
|
||||
modifier = if (state.query.isEmpty()) Modifier else Modifier.weight(1f),
|
||||
)
|
||||
ClearButton(
|
||||
state = state,
|
||||
|
|
@ -217,58 +217,45 @@ private fun DecorationBox(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun Field(state: SearchBarUM, innerTextField: @Composable () -> Unit) {
|
||||
private fun Field(state: SearchBarUM, modifier: Modifier = Modifier, innerTextField: @Composable () -> Unit) {
|
||||
Box(
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.heightIn(min = TangemTheme.dimens2.x5)
|
||||
.width(IntrinsicSize.Max),
|
||||
.then(if (state.query.isEmpty()) Modifier.width(IntrinsicSize.Max) else Modifier),
|
||||
) {
|
||||
innerTextField()
|
||||
|
||||
val placeholderOpacity by remember(state.query) {
|
||||
derivedStateOf {
|
||||
if (state.query.isNotEmpty()) {
|
||||
0f
|
||||
} else {
|
||||
1f
|
||||
}
|
||||
}
|
||||
if (state.query.isEmpty()) {
|
||||
Text(
|
||||
text = state.placeholderText.resolveReference(),
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.testTag(SearchBarTestTags.PLACEHOLDER_TEXT),
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = state.placeholderText.resolveReference(),
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.testTag(SearchBarTestTags.PLACEHOLDER_TEXT)
|
||||
.alpha(placeholderOpacity),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ClearButton(state: SearchBarUM) {
|
||||
if (state.query.isNotEmpty()) {
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_close_new_20),
|
||||
tint = TangemTheme.colors2.graphic.neutral.tertiary,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.size(TangemTheme.dimens2.x5)
|
||||
.clip(CircleShape)
|
||||
.clickable(
|
||||
onClick = state.onClearClick,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(bounded = false),
|
||||
)
|
||||
.testTag(SearchBarTestTags.CLEAR_BUTTON),
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_close_new_20),
|
||||
tint = TangemTheme.colors2.graphic.neutral.tertiary,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x5)
|
||||
.clip(CircleShape)
|
||||
.clickable(
|
||||
onClick = state.onClearClick,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(bounded = false),
|
||||
)
|
||||
.testTag(SearchBarTestTags.CLEAR_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,13 +11,15 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.takeOrElse
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_cloud_24_filled
|
||||
|
||||
private const val DEFAULT_DEVICE_ICON_COLOR = 0xFF595963
|
||||
private const val DEFAULT_BORDER_COLOR = 0x1A1E1E1E
|
||||
|
||||
/**
|
||||
* Composable function for displaying a wallet icon based on the provided [DeviceIconUM] state.
|
||||
|
|
@ -57,9 +59,9 @@ fun TangemDeviceIcon(state: DeviceIconUM, modifier: Modifier = Modifier) {
|
|||
)
|
||||
DeviceIconUM.Mobile -> Icon(
|
||||
modifier = modifier,
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_shield_24),
|
||||
imageVector = Icons.ic_cloud_24_filled,
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.status.attention,
|
||||
tint = Color(DEFAULT_DEVICE_ICON_COLOR),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -73,10 +75,10 @@ private fun DeviceIcon(
|
|||
tColor: Color?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val main = mainColor.takeOrElse { TangemTheme.colors2.graphic.neutral.tertiaryConstant }
|
||||
val second = secondColor?.takeOrElse { TangemTheme.colors2.graphic.neutral.tertiaryConstant }
|
||||
val third = thirdColor?.takeOrElse { TangemTheme.colors2.graphic.neutral.tertiaryConstant }
|
||||
val borderColor = TangemTheme.colors2.border.walletIcon
|
||||
val main = mainColor.takeOrElse { Color(DEFAULT_DEVICE_ICON_COLOR) }
|
||||
val second = secondColor?.takeOrElse { Color(DEFAULT_DEVICE_ICON_COLOR) }
|
||||
val third = thirdColor?.takeOrElse { Color(DEFAULT_DEVICE_ICON_COLOR) }
|
||||
val borderColor = Color(DEFAULT_BORDER_COLOR)
|
||||
|
||||
val imageVector = remember(isRing, main, second, third, borderColor, tColor) {
|
||||
when {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ fun TangemTab(
|
|||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(),
|
||||
)
|
||||
.padding(all = TangemTheme.dimens2.x3),
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens2.x2,
|
||||
horizontal = TangemTheme.dimens2.x3,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -108,7 +108,7 @@ fun TangemButton(
|
|||
enabled = isEnabled,
|
||||
color = backgroundColor,
|
||||
border = resolveBorder(isFocused = isFocused, colorTokens = colorTokens, contentAlpha = contentAlpha),
|
||||
shape = RoundedCornerShape(999.dp),
|
||||
shape = CircleShape,
|
||||
interactionSource = interactionSource,
|
||||
isMaterial = variant == TangemButton.Variant.Material,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ private fun CloseButton(onClick: () -> Unit) {
|
|||
@Composable
|
||||
private fun Preview(@PreviewParameter(TangemSearchStateProvider::class) state: TangemSearch.State) {
|
||||
TangemThemePreviewRedesign {
|
||||
Box(modifier = Modifier.background(TangemTheme.colors3.bg.secondary)) {
|
||||
Box(modifier = Modifier.background(TangemTheme.colors3.bg.tertiary)) {
|
||||
TangemSearch(
|
||||
state = state,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -12,8 +12,12 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.LinearGradientShader
|
||||
import androidx.compose.ui.graphics.Shader
|
||||
import androidx.compose.ui.graphics.ShaderBrush
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -113,7 +117,7 @@ fun TangemSurface(
|
|||
@Composable
|
||||
private fun Modifier.materialShadow(shape: Shape): Modifier = softLayerShadow(
|
||||
radius = 40.dp,
|
||||
color = Color.Black.copy(alpha = 0.10f),
|
||||
color = Color.Black.copy(alpha = 0.12f),
|
||||
shape = shape,
|
||||
spread = 0.dp,
|
||||
offset = DpOffset(x = 0.dp, y = 8.dp),
|
||||
|
|
@ -141,19 +145,22 @@ private fun Modifier.materialBorder(shape: Shape): Modifier = border(
|
|||
@Composable
|
||||
private fun Modifier.materialFill(): Modifier {
|
||||
val isBlurEnabled = LocalHazeState.current.blurEnabled
|
||||
val material = TangemTheme.colors3.material
|
||||
val hazed = hazeEffectTangem(
|
||||
style = HazeStyle(
|
||||
backgroundColor = TangemTheme.colors3.material.fill.blur,
|
||||
backgroundColor = Color.Transparent,
|
||||
blurRadius = 32.dp,
|
||||
tints = emptyList(),
|
||||
tints = listOf(
|
||||
HazeTint(material.fill.blur),
|
||||
),
|
||||
),
|
||||
) {
|
||||
fallbackTint = HazeTint(Color.Transparent)
|
||||
}
|
||||
return hazed.conditionalCompose(!isBlurEnabled) {
|
||||
// Paint the opaque fill first, then layer the translucent tint on top so both are visible.
|
||||
background(TangemTheme.colors3.material.fill.solid)
|
||||
.background(TangemTheme.colors3.material.tint.solid)
|
||||
background(material.fill.solid)
|
||||
.background(material.tint.solid)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -162,13 +169,22 @@ private fun Modifier.materialFill(): Modifier {
|
|||
@ReadOnlyComposable
|
||||
private fun materialBorderBrush(): Brush {
|
||||
val border = TangemTheme.colors3.material.border
|
||||
return Brush.linearGradient(
|
||||
0f to border.start,
|
||||
0.5f to border.mid,
|
||||
1f to border.end,
|
||||
start = Offset.Zero,
|
||||
end = Offset.Infinite,
|
||||
)
|
||||
val startColor = border.start
|
||||
val midColor = Color.Transparent
|
||||
val endColor = border.end
|
||||
return object : ShaderBrush() {
|
||||
override fun createShader(size: Size): Shader {
|
||||
val w = size.width
|
||||
val h = size.height
|
||||
val k = 2f * w * h / (w * w + h * h)
|
||||
return LinearGradientShader(
|
||||
from = Offset.Zero,
|
||||
to = Offset(x = k * h, y = k * w),
|
||||
colors = listOf(startColor, midColor, midColor, endColor),
|
||||
colorStops = listOf(0f, 0.40f, 0.60f, 1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ private enum class SlotId { Start, Content, Group, End }
|
|||
* @param contentAlign How [contentColumn] is aligned horizontally within the bar.
|
||||
* @param windowInsets Top inset applied above the row. Pass `WindowInsets(0)` inside a bottom
|
||||
* sheet / modal.
|
||||
* @param contentPadding Inner padding applied to the row, inside [windowInsets]. Defaults to
|
||||
* [TangemTopNavigation.DefaultContentPadding] (top 8, bottom 16, horizontal 16). Override a single
|
||||
* edge via [com.tangem.core.ui.extensions.copy], e.g. `DefaultContentPadding.copy(top = 16.dp)`.
|
||||
* @param blurBackground Whether the fade behind the row should blur the content below.
|
||||
* @param startButton Leading slot. Typically a back button (see [TangemButton.Back]).
|
||||
* @param endButtonsGroup Optional pill-grouped secondary actions placed just before [endButton].
|
||||
|
|
@ -59,6 +62,7 @@ fun TangemTopNavigation(
|
|||
modifier: Modifier = Modifier,
|
||||
contentAlign: TangemTopNavigation.ContentAlign = TangemTopNavigation.ContentAlign.Start,
|
||||
windowInsets: WindowInsets = WindowInsets.statusBars,
|
||||
contentPadding: PaddingValues = TangemTopNavigation.DefaultContentPadding,
|
||||
blurBackground: Boolean = true,
|
||||
startButton: (@Composable () -> Unit)? = null,
|
||||
endButtonsGroup: (@Composable RowScope.() -> Unit)? = null,
|
||||
|
|
@ -89,12 +93,7 @@ fun TangemTopNavigation(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(windowInsets)
|
||||
.padding(
|
||||
top = 8.dp,
|
||||
bottom = 16.dp,
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
),
|
||||
.padding(contentPadding),
|
||||
content = {
|
||||
val displayedStart = rememberLastNonNull(startButton)
|
||||
Box(modifier = Modifier.layoutId(SlotId.Start)) {
|
||||
|
|
@ -208,6 +207,7 @@ fun TangemTopNavigation(
|
|||
subtitle: TextReference? = null,
|
||||
contentAlign: TangemTopNavigation.ContentAlign = TangemTopNavigation.ContentAlign.Start,
|
||||
windowInsets: WindowInsets = WindowInsets.statusBars,
|
||||
contentPadding: PaddingValues = TangemTopNavigation.DefaultContentPadding,
|
||||
blurBackground: Boolean = true,
|
||||
onBack: (() -> Unit)? = null,
|
||||
endButtonsGroup: (@Composable RowScope.() -> Unit)? = null,
|
||||
|
|
@ -217,6 +217,7 @@ fun TangemTopNavigation(
|
|||
modifier = modifier,
|
||||
contentAlign = contentAlign,
|
||||
windowInsets = windowInsets,
|
||||
contentPadding = contentPadding,
|
||||
blurBackground = blurBackground,
|
||||
startButton = onBack?.let { { TangemButton.Back(onClick = it) } },
|
||||
endButtonsGroup = endButtonsGroup,
|
||||
|
|
@ -233,6 +234,7 @@ fun TangemTopNavigation(
|
|||
subtitle: TextReference? = null,
|
||||
contentAlign: TangemTopNavigation.ContentAlign = TangemTopNavigation.ContentAlign.Start,
|
||||
windowInsets: WindowInsets = WindowInsets.statusBars,
|
||||
contentPadding: PaddingValues = TangemTopNavigation.DefaultContentPadding,
|
||||
blurBackground: Boolean = true,
|
||||
endButtonsGroup: (@Composable RowScope.() -> Unit)? = null,
|
||||
onClose: (() -> Unit)? = null,
|
||||
|
|
@ -242,6 +244,7 @@ fun TangemTopNavigation(
|
|||
modifier = modifier,
|
||||
contentAlign = contentAlign,
|
||||
windowInsets = windowInsets,
|
||||
contentPadding = contentPadding,
|
||||
blurBackground = blurBackground,
|
||||
startButton = startButton,
|
||||
endButtonsGroup = endButtonsGroup,
|
||||
|
|
@ -258,6 +261,7 @@ fun TangemTopNavigation(
|
|||
subtitle: TextReference? = null,
|
||||
contentAlign: TangemTopNavigation.ContentAlign = TangemTopNavigation.ContentAlign.Start,
|
||||
windowInsets: WindowInsets = WindowInsets.statusBars,
|
||||
contentPadding: PaddingValues = TangemTopNavigation.DefaultContentPadding,
|
||||
blurBackground: Boolean = true,
|
||||
onBack: (() -> Unit)? = null,
|
||||
endButton: @Composable () -> Unit,
|
||||
|
|
@ -266,6 +270,7 @@ fun TangemTopNavigation(
|
|||
modifier = modifier,
|
||||
contentAlign = contentAlign,
|
||||
windowInsets = windowInsets,
|
||||
contentPadding = contentPadding,
|
||||
blurBackground = blurBackground,
|
||||
startButton = onBack?.let { { TangemButton.Back(onClick = it) } },
|
||||
endButton = endButton,
|
||||
|
|
@ -308,7 +313,7 @@ private fun ColumnScope.TitleSubtitle(title: TextReference, subtitle: TextRefere
|
|||
) {
|
||||
displayedSubtitle?.let { text ->
|
||||
Column {
|
||||
Spacer(Modifier.height(2.dp))
|
||||
Spacer(Modifier.height(4.dp))
|
||||
TangemNavigationText(text = text, role = TangemNavigationText.Role.Subtitle)
|
||||
}
|
||||
}
|
||||
|
|
@ -317,6 +322,15 @@ private fun ColumnScope.TitleSubtitle(title: TextReference, subtitle: TextRefere
|
|||
|
||||
object TangemTopNavigation {
|
||||
|
||||
/** Default inner padding of the row: top 8, bottom 16, horizontal 16. */
|
||||
@Suppress("MagicNumber")
|
||||
val DefaultContentPadding: PaddingValues = PaddingValues(
|
||||
top = 8.dp,
|
||||
bottom = 16.dp,
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
)
|
||||
|
||||
/** Horizontal alignment of the center content slot. */
|
||||
enum class ContentAlign {
|
||||
Start, Center
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
||||
/**
|
||||
* Returns a copy of this [PaddingValues] with the given edges overridden, leaving the rest unchanged.
|
||||
*/
|
||||
@Composable
|
||||
fun PaddingValues.copy(start: Dp? = null, top: Dp? = null, end: Dp? = null, bottom: Dp? = null): PaddingValues {
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
return PaddingValues(
|
||||
start = start ?: calculateStartPadding(layoutDirection),
|
||||
top = top ?: calculateTopPadding(),
|
||||
end = end ?: calculateEndPadding(layoutDirection),
|
||||
bottom = bottom ?: calculateBottomPadding(),
|
||||
)
|
||||
}
|
||||
|
|
@ -52,11 +52,13 @@ open class BigDecimalCryptoFormatStyled(
|
|||
fun BigDecimalFormatScope.crypto(
|
||||
symbol: String,
|
||||
decimals: Int,
|
||||
ignoreSymbolPosition: Boolean = false,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): BigDecimalCryptoFormat {
|
||||
return BigDecimalCryptoFormat(
|
||||
symbol = symbol,
|
||||
decimals = decimals,
|
||||
shouldIgnoreSymbolPosition = ignoreSymbolPosition,
|
||||
locale = locale,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,5 @@ package com.tangem.core.ui.test
|
|||
|
||||
object AppSettingsScreenTestTags {
|
||||
const val CURRENCY_BUTTON = "APP_SETTINGS_SCREEN_CURRENCY_BUTTON"
|
||||
const val BACK_BUTTON = "APP_SETTINGS_SCREEN_BACK_BUTTON"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8 KiB |
Loading…
Add table
Add a link
Reference in a new issue