Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-17 20:20:52 +05:00
parent 5da322576b
commit c037bec366
15 changed files with 782 additions and 116 deletions

View file

@ -80,6 +80,7 @@ fun GhostTangemButton(
contentColor = contentColor, contentColor = contentColor,
isEnabled = isEnabled, isEnabled = isEnabled,
isLoading = isLoading, isLoading = isLoading,
hasPadding = false,
size = size, size = size,
iconPosition = iconPosition, iconPosition = iconPosition,
iconRes = iconRes, iconRes = iconRes,

View file

@ -43,8 +43,9 @@ private const val LOADING_ANIMATION_DURATION = 150
* @param text TextReference for the button label. * @param text TextReference for the button label.
* @param iconRes Drawable resource ID for the icon to be displayed in the button. * @param iconRes Drawable resource ID for the icon to be displayed in the button.
* @param iconPosition Position of the icon (Start or End). * @param iconPosition Position of the icon (Start or End).
* @param isEnabled Boolean indicating whether the button is enabled. * @param isEnabled Boolean indicating whether the button is enabled.
* @param isLoading Boolean indicating whether the button is in a loading state. * @param isLoading Boolean indicating whether the button is in a loading state.
* @param hasPadding Boolean indicating whether the button should have padding around its content.
* @param contentColor Color of the button content (text and icon). * @param contentColor Color of the button content (text and icon).
* @param size TangemButtonSize defining the size of the button. * @param size TangemButtonSize defining the size of the button.
* *
@ -60,6 +61,7 @@ internal fun TangemButtonInternal(
iconPosition: TangemButtonIconPosition = TangemButtonIconPosition.Start, iconPosition: TangemButtonIconPosition = TangemButtonIconPosition.Start,
isEnabled: Boolean = true, isEnabled: Boolean = true,
isLoading: Boolean = false, isLoading: Boolean = false,
hasPadding: Boolean = true,
contentColor: Color = TangemTheme.colors2.text.neutral.primary, contentColor: Color = TangemTheme.colors2.text.neutral.primary,
size: TangemButtonSize = TangemButtonSize.X15, size: TangemButtonSize = TangemButtonSize.X15,
) { ) {
@ -72,7 +74,7 @@ internal fun TangemButtonInternal(
.conditionalCompose(text == null) { .conditionalCompose(text == null) {
width(size.toHeightDp()) width(size.toHeightDp())
} }
.conditionalCompose(text != null) { .conditionalCompose(text != null && hasPadding) {
padding(size.toPaddingDp()) padding(size.toPaddingDp())
} }
.animateContentSize(), .animateContentSize(),

View file

@ -0,0 +1,346 @@
package com.tangem.core.ui.ds.field.search
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.shrinkHorizontally
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.*
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
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.SoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.input.ImeAction
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.PreviewParameterProvider
import androidx.compose.ui.util.fastForEach
import com.tangem.core.ui.R
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.ds.button.GhostTangemButton
import com.tangem.core.ui.ds.button.TangemButtonSize
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.test.BaseSearchBarTestTags
import com.tangem.core.ui.test.SearchBarTestTags
/**
* Shape options for [TangemSearchField].
*/
enum class TangemFieldShape {
RoundedCorners,
Circle,
;
@ReadOnlyComposable
@Composable
internal fun toShape(): Shape {
return when (this) {
RoundedCorners -> RoundedCornerShape(TangemTheme.dimens2.x4)
Circle -> CircleShape
}
}
}
/**
* Custom search field component that provides a user-friendly interface for searching and filtering content.
*
* [Figma](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8445-19217&m=dev)
*
* @param state The state of the search field, including the current query, placeholder text, and active status.
* @param shape The shape of the search field, which can be either rounded corners or a circle.
* @param modifier The modifier to be applied to the search field.
* @param enabled Whether the search field is enabled or not.
* @param focusRequester The [FocusRequester] used to request focus on the search field when it becomes active.
*/
@Composable
fun TangemSearchField(
state: SearchBarUM,
shape: TangemFieldShape,
modifier: Modifier = Modifier,
enabled: Boolean = true,
focusRequester: FocusRequester = remember { FocusRequester() },
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val interactionSource = remember { MutableInteractionSource() }
var isInitialComposition by rememberSaveable { mutableStateOf(true) }
LaunchedEffect(Unit) {
isInitialComposition = false
}
BasicTextField(
modifier = modifier
.heightIn(min = TangemTheme.dimens2.x11)
.onFocusChanged { focusState ->
if (!isInitialComposition) {
if (focusState.isFocused) {
state.onActiveChange(true)
} else {
state.onActiveChange(false)
}
}
}
.focusRequester(focusRequester)
.testTag(BaseSearchBarTestTags.SEARCH_BAR),
enabled = enabled,
value = state.query,
onValueChange = state.onQueryChange,
interactionSource = interactionSource,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Search,
),
keyboardActions = KeyboardActions(
onSearch = {
keyboardController?.hide()
focusManager.clearFocus()
},
),
singleLine = true,
maxLines = 1,
textStyle = TangemTheme.typography2.bodySemibold16.copy(
color = TangemTheme.colors2.text.neutral.primary,
),
cursorBrush = SolidColor(TangemTheme.colors2.graphic.neutral.primary),
decorationBox = @Composable { innerTextField ->
DecorationBox(
state = state,
shape = shape,
innerTextField = innerTextField,
focusManager = focusManager,
interactionSource = interactionSource,
color = TangemTheme.colors2.field.backgroundDefault,
keyboardController = keyboardController,
)
},
)
}
@Suppress("LongParameterList")
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun DecorationBox(
state: SearchBarUM,
shape: TangemFieldShape,
focusManager: FocusManager,
interactionSource: MutableInteractionSource,
color: Color,
keyboardController: SoftwareKeyboardController?,
innerTextField: @Composable () -> Unit,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
) {
val isFocused by interactionSource.collectIsFocusedAsState()
val alignmentBias by animateFloatAsState(
targetValue = if (isFocused) -1f else 0f,
animationSpec = tween(),
label = "AlignmentBias",
)
Box(
contentAlignment = BiasAlignment(horizontalBias = alignmentBias, verticalBias = 0f),
modifier = Modifier
.weight(1f)
.background(color, shape.toShape())
.padding(TangemTheme.dimens2.x3),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
) {
Icon(
modifier = Modifier
.size(TangemTheme.dimens2.x5)
.testTag(SearchBarTestTags.ICON),
painter = painterResource(id = R.drawable.ic_search_default_24),
tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant,
contentDescription = null,
)
Field(
state = state,
innerTextField = innerTextField,
)
ClearButton(
state = state,
)
}
}
CancelButton(
state = state,
keyboardController = keyboardController,
focusManager = focusManager,
isActive = isFocused,
)
}
}
@Composable
private fun Field(state: SearchBarUM, innerTextField: @Composable () -> Unit) {
Box(
contentAlignment = Alignment.CenterStart,
modifier = Modifier
.heightIn(min = TangemTheme.dimens2.x5)
.width(IntrinsicSize.Max),
) {
innerTextField()
val placeholderOpacity by remember(state.query) {
derivedStateOf {
if (state.query.isNotEmpty()) {
0f
} else {
1f
}
}
}
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),
)
}
}
}
@Composable
private fun CancelButton(
state: SearchBarUM,
isActive: Boolean,
focusManager: FocusManager,
keyboardController: SoftwareKeyboardController?,
modifier: Modifier = Modifier,
) {
AnimatedVisibility(
visible = isActive,
enter = expandHorizontally(),
exit = shrinkHorizontally(),
modifier = modifier,
) {
GhostTangemButton(
text = resourceReference(R.string.common_cancel),
size = TangemButtonSize.X9,
modifier = Modifier.padding(start = TangemTheme.dimens2.x3),
onClick = {
if (state.query.isNotEmpty()) {
state.onQueryChange("")
}
focusManager.clearFocus()
keyboardController?.hide()
state.onActiveChange(false)
state.onClearClick()
},
)
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun TangemSearchField_Preview(@PreviewParameter(TangemSearchFieldPreviewProvider::class) params: SearchBarUM) {
var state by remember { mutableStateOf(params) }
TangemThemePreviewRedesign {
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x4),
modifier = Modifier
.background(TangemTheme.colors2.surface.level1)
.padding(TangemTheme.dimens2.x4),
) {
TangemFieldShape.entries.fastForEach { shape ->
TangemSearchField(
state = state.copy(
onActiveChange = { state = state.copy(isActive = it) },
onQueryChange = { state = state.copy(query = it) },
onClearClick = { state = state.copy(query = "") },
),
shape = shape,
)
}
}
}
}
private class TangemSearchFieldPreviewProvider : PreviewParameterProvider<SearchBarUM> {
override val values: Sequence<SearchBarUM>
get() = sequenceOf(
SearchBarUM(
placeholderText = stringReference("Crypto, news and more"),
query = "BTC",
onQueryChange = {},
isActive = true,
onActiveChange = {},
),
SearchBarUM(
placeholderText = stringReference("Crypto, news and more"),
query = "",
onQueryChange = {},
isActive = false,
onActiveChange = {},
),
)
}
// endregion

View file

@ -5,23 +5,18 @@ import androidx.annotation.DrawableRes
import androidx.compose.animation.* import androidx.compose.animation.*
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import com.tangem.core.ui.R import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.* import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
@ -48,6 +43,7 @@ fun TangemTopBar(
title: TextReference? = null, title: TextReference? = null,
subtitle: TextReference? = null, subtitle: TextReference? = null,
startAction: TangemTopBarActionUM? = null, startAction: TangemTopBarActionUM? = null,
type: TangemTopBarType = TangemTopBarType.Default,
endActions: ImmutableList<TangemTopBarActionUM> = persistentListOf(), endActions: ImmutableList<TangemTopBarActionUM> = persistentListOf(),
@DrawableRes titleIconRes: Int? = null, @DrawableRes titleIconRes: Int? = null,
) { ) {
@ -55,17 +51,21 @@ fun TangemTopBar(
title = title, title = title,
subtitle = subtitle, subtitle = subtitle,
titleIconRes = titleIconRes, titleIconRes = titleIconRes,
type = type,
modifier = modifier, modifier = modifier,
startContent = if (startAction != null) { startContent = if (startAction != null) {
{ TangemTopBarActionContent(startAction) } { TangemTopBarActionContent(actionUM = startAction, type = type) }
} else { } else {
null null
}, },
endContent = if (endActions.isNotEmpty()) { endContent = if (endActions.isNotEmpty()) {
{ {
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x5)) { Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x5),
) {
endActions.forEach { action -> endActions.forEach { action ->
TangemTopBarActionContent(action) TangemTopBarActionContent(actionUM = action, type = type)
} }
} }
} }
@ -88,54 +88,88 @@ fun TangemTopBar(
@Composable @Composable
fun TangemTopBar( fun TangemTopBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
type: TangemTopBarType = TangemTopBarType.Default,
title: TextReference? = null, title: TextReference? = null,
subtitle: TextReference? = null, subtitle: TextReference? = null,
@DrawableRes titleIconRes: Int? = null, @DrawableRes titleIconRes: Int? = null,
startContent: @Composable (() -> Unit)? = null, startContent: @Composable (() -> Unit)? = null,
endContent: @Composable (() -> Unit)? = null, endContent: @Composable (() -> Unit)? = null,
) { ) {
Box( TangemTopBar(
modifier = modifier,
type = type,
startContent = startContent,
endContent = endContent,
content = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x0_5),
) {
TangemTopBarTitle(title = title, titleIconRes = titleIconRes)
AnimatedVisibility(
visible = subtitle != null,
label = "Subtitle Visibility",
) {
val wrappedSubtitle = remember(this) { requireNotNull(subtitle) }
Text(
text = wrappedSubtitle.resolveAnnotatedReference(),
color = TangemTheme.colors2.text.neutral.secondary,
style = TangemTheme.typography2.bodyRegular15,
textAlign = TextAlign.Center,
maxLines = 1,
)
}
}
},
)
}
/**
* A top bar composable that displays a title and optional start and end icons.
* [Figma](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8435-74860&m=dev)
*
* @param modifier Modifier to be applied to the top bar.
* @param type Type of the top bar, which determines its size and padding.
* @param content Composable content to be displayed in the center of the top bar
* @param startContent Optional composable content to be displayed at the start (left) of the top bar.
* @param endContent Optional composable content to be displayed at the end (right) of the top bar.
*/
@Composable
fun TangemTopBar(
modifier: Modifier = Modifier,
type: TangemTopBarType = TangemTopBarType.Default,
content: @Composable () -> Unit,
startContent: @Composable (() -> Unit)? = null,
endContent: @Composable (() -> Unit)? = null,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.height(TangemTheme.dimens2.x16) .heightIn(min = type.getSize())
.padding(TangemTheme.dimens2.x4, TangemTheme.dimens2.x3), .padding(type.getPadding()),
) { ) {
AnimatedVisibility( AnimatedContent(
visible = startContent != null, targetState = startContent != null,
modifier = Modifier.align(Alignment.CenterStart), modifier = Modifier.size(TangemTheme.dimens2.x11),
label = "Start Content Visibility", label = "Start Content Visibility",
) { ) { isVisible ->
startContent?.invoke() if (isVisible) {
} startContent?.invoke()
Column(
modifier = Modifier
.align(Alignment.Center)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x0_5),
) {
TangemTopBarTitle(title = title, titleIconRes = titleIconRes)
AnimatedVisibility(
visible = subtitle != null,
label = "Subtitle Visibility",
) {
val wrappedSubtitle = remember(this) { requireNotNull(subtitle) }
Text(
text = wrappedSubtitle.resolveAnnotatedReference(),
color = TangemTheme.colors2.text.neutral.secondary,
style = TangemTheme.typography2.bodyRegular15,
textAlign = TextAlign.Center,
maxLines = 1,
)
} }
} }
AnimatedVisibility(
visible = endContent != null, content()
modifier = Modifier.align(Alignment.CenterEnd),
AnimatedContent(
targetState = endContent != null,
modifier = Modifier.size(TangemTheme.dimens2.x11),
label = "End Content Visibility", label = "End Content Visibility",
) { ) { isVisible ->
endContent?.invoke() if (isVisible) {
endContent?.invoke()
}
} }
} }
} }
@ -151,7 +185,6 @@ private fun TangemTopBarTitle(title: TextReference?, @DrawableRes titleIconRes:
val wrappedTitle = remember(this) { requireNotNull(title) } val wrappedTitle = remember(this) { requireNotNull(title) }
Row( Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy( horizontalArrangement = Arrangement.spacedBy(
space = TangemTheme.dimens2.x1, space = TangemTheme.dimens2.x1,
alignment = Alignment.CenterHorizontally, alignment = Alignment.CenterHorizontally,
@ -184,33 +217,6 @@ private fun TangemTopBarTitle(title: TextReference?, @DrawableRes titleIconRes:
} }
} }
@Composable
fun TangemTopBarActionContent(
actionUM: TangemTopBarActionUM,
modifier: Modifier = Modifier,
iconSize: Dp = TangemTheme.dimens2.x8,
) {
val background = lerp(
start = Color.Transparent,
stop = TangemTheme.colors2.button.backgroundSecondary,
fraction = actionUM.ghostModeProgress,
)
val padding = (TangemTheme.dimens2.x10 - iconSize) / 2
Icon(
imageVector = ImageVector.vectorResource(id = actionUM.iconRes),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primary,
modifier = modifier
.size(TangemTheme.dimens2.x10)
.clip(CircleShape)
.conditional(actionUM.isActionable) { background(background) }
.conditionalCompose(actionUM.isActionable && actionUM.onClick != null) {
clickableSingle(onClick = requireNotNull(actionUM.onClick))
}
.padding(padding),
)
}
// region Preview // region Preview
@Composable @Composable
@Preview(showBackground = true, widthDp = 375) @Preview(showBackground = true, widthDp = 375)
@ -221,6 +227,7 @@ private fun TangemTopBar_Preview(@PreviewParameter(PreviewProvider::class) param
title = params.title, title = params.title,
subtitle = params.subtitle, subtitle = params.subtitle,
titleIconRes = params.titleIconRes, titleIconRes = params.titleIconRes,
type = TangemTopBarType.BottomSheet,
modifier = Modifier.background(TangemTheme.colors2.surface.level1), modifier = Modifier.background(TangemTheme.colors2.surface.level1),
startContent = params.startActionUM?.let { { TangemTopBarActionContent(it) } }, startContent = params.startActionUM?.let { { TangemTopBarActionContent(it) } },
endContent = if (params.endActions.isNotEmpty()) { endContent = if (params.endActions.isNotEmpty()) {
@ -256,11 +263,13 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
onClick = {}, onClick = {},
isActionable = false, isActionable = false,
), ),
endActions = persistentListOf(TangemTopBarActionUM( endActions = persistentListOf(
iconRes = R.drawable.ic_more_default_24, TangemTopBarActionUM(
onClick = {}, iconRes = R.drawable.ic_more_default_24,
isActionable = false, onClick = {},
)), isActionable = false,
),
),
), ),
TangemTopBarPreviewData( TangemTopBarPreviewData(
title = stringReference("Title"), title = stringReference("Title"),
@ -271,12 +280,14 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
isActionable = true, isActionable = true,
ghostModeProgress = 1f, ghostModeProgress = 1f,
), ),
endActions = persistentListOf(TangemTopBarActionUM( endActions = persistentListOf(
iconRes = R.drawable.ic_more_default_24, TangemTopBarActionUM(
onClick = {}, iconRes = R.drawable.ic_more_default_24,
isActionable = true, onClick = {},
ghostModeProgress = 1f, isActionable = true,
)), ghostModeProgress = 1f,
),
),
), ),
TangemTopBarPreviewData( TangemTopBarPreviewData(
title = stringReference("Title"), title = stringReference("Title"),
@ -287,12 +298,14 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
onClick = {}, onClick = {},
isActionable = false, isActionable = false,
), ),
endActions = persistentListOf(TangemTopBarActionUM( endActions = persistentListOf(
iconRes = R.drawable.ic_more_default_24, TangemTopBarActionUM(
onClick = {}, iconRes = R.drawable.ic_more_default_24,
isActionable = true, onClick = {},
ghostModeProgress = 1f, isActionable = true,
)), ghostModeProgress = 1f,
),
),
), ),
TangemTopBarPreviewData( TangemTopBarPreviewData(
subtitle = stringReference("Subtitle"), subtitle = stringReference("Subtitle"),
@ -302,19 +315,23 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
onClick = {}, onClick = {},
isActionable = true, isActionable = true,
), ),
endActions = persistentListOf(TangemTopBarActionUM( endActions = persistentListOf(
iconRes = R.drawable.ic_more_default_24, TangemTopBarActionUM(
onClick = {}, iconRes = R.drawable.ic_more_default_24,
isActionable = false, onClick = {},
)), isActionable = false,
),
),
), ),
TangemTopBarPreviewData( TangemTopBarPreviewData(
title = stringReference("Title"), title = stringReference("Title"),
endActions = persistentListOf(TangemTopBarActionUM( endActions = persistentListOf(
iconRes = R.drawable.ic_more_default_24, TangemTopBarActionUM(
onClick = {}, iconRes = R.drawable.ic_more_default_24,
isActionable = false, onClick = {},
)), isActionable = false,
),
),
), ),
TangemTopBarPreviewData( TangemTopBarPreviewData(
title = stringReference("Title"), title = stringReference("Title"),
@ -353,11 +370,13 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
onClick = {}, onClick = {},
isActionable = true, isActionable = true,
), ),
endActions = persistentListOf(TangemTopBarActionUM( endActions = persistentListOf(
iconRes = R.drawable.ic_more_default_24, TangemTopBarActionUM(
onClick = {}, iconRes = R.drawable.ic_more_default_24,
isActionable = false, onClick = {},
)), isActionable = false,
),
),
), ),
) )
} }

View file

@ -0,0 +1,53 @@
package com.tangem.core.ui.ds.topbar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import com.tangem.core.ui.extensions.clickableSingle
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.conditionalCompose
import com.tangem.core.ui.res.TangemTheme
/**
* Composable for top bar action content
*
* @param actionUM user model for top bar action
* @param modifier modifier for this composable
* @param type type of top bar, affects size and padding of action content
*/
@Composable
fun TangemTopBarActionContent(
actionUM: TangemTopBarActionUM,
modifier: Modifier = Modifier,
type: TangemTopBarType = TangemTopBarType.Default,
) {
val background = lerp(
start = Color.Transparent,
stop = TangemTheme.colors2.button.backgroundSecondary,
fraction = actionUM.ghostModeProgress,
)
val padding = (TangemTheme.dimens2.x11 - type.getSideContentSize()) / 2
Icon(
imageVector = ImageVector.vectorResource(id = actionUM.iconRes),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primary,
modifier = modifier
.size(TangemTheme.dimens2.x11)
.clip(CircleShape)
.conditional(actionUM.isActionable) { background(background) }
.conditionalCompose(actionUM.isActionable && actionUM.onClick != null) {
clickableSingle(onClick = requireNotNull(actionUM.onClick))
}
.padding(padding),
)
}

View file

@ -0,0 +1,45 @@
package com.tangem.core.ui.ds.topbar
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.unit.Dp
import com.tangem.core.ui.res.TangemTheme
/**
* Type of top bar, affects size and padding of content
*
* @see TangemTopBar
*/
enum class TangemTopBarType {
Default,
BottomSheet,
;
@ReadOnlyComposable
@Composable
fun getSize(): Dp {
return when (this) {
Default -> TangemTheme.dimens2.x16
BottomSheet -> TangemTheme.dimens2.x19
}
}
@ReadOnlyComposable
@Composable
fun getSideContentSize(): Dp {
return when (this) {
Default -> TangemTheme.dimens2.x8
BottomSheet -> TangemTheme.dimens2.x7
}
}
@ReadOnlyComposable
@Composable
fun getPadding(): PaddingValues {
return when (this) {
Default -> PaddingValues(horizontal = TangemTheme.dimens2.x4, vertical = TangemTheme.dimens2.x3)
BottomSheet -> PaddingValues(TangemTheme.dimens2.x4)
}
}
}

View file

@ -3,6 +3,8 @@
package com.tangem.core.ui.res package com.tangem.core.ui.res
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -31,15 +33,27 @@ fun TangemThemeRedesign(content: @Composable () -> Unit) {
LocalTangemTypography2 provides TangemTypography2(InterFamily), LocalTangemTypography2 provides TangemTypography2(InterFamily),
LocalRootBackgroundColor provides remember(rootBackgroundColor) { mutableStateOf(rootBackgroundColor) }, LocalRootBackgroundColor provides remember(rootBackgroundColor) { mutableStateOf(rootBackgroundColor) },
) { ) {
ProvideHaze { CompositionLocalProvider(
Box(Modifier.hazeSourceTangem(zIndex = 1f)) { LocalTextSelectionColors provides TangemTextSelectionColors2,
content() ) {
ProvideHaze {
Box(Modifier.hazeSourceTangem(zIndex = 1f)) {
content()
}
} }
} }
} }
} }
} }
private val TangemTextSelectionColors2: TextSelectionColors
@Composable
@ReadOnlyComposable
get() = TextSelectionColors(
handleColor = TangemTheme.colors2.text.neutral.primary,
backgroundColor = TangemTheme.colors2.text.neutral.primary.copy(alpha = 0.3f),
)
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun lightThemeColors2(): TangemColors2 { private fun lightThemeColors2(): TangemColors2 {
@ -48,7 +62,7 @@ private fun lightThemeColors2(): TangemColors2 {
primary = TangemColorPalette.Dark6, primary = TangemColorPalette.Dark6,
primaryInverted = TangemColorPalette.White, primaryInverted = TangemColorPalette.White,
secondary = TangemColorPalette.Dark2, secondary = TangemColorPalette.Dark2,
tertiary = TangemColorPalette.Dark3, tertiary = TangemColorPalette.Dark1,
primaryInvertedConstant = TangemColorPalette.White, primaryInvertedConstant = TangemColorPalette.White,
), ),
status = TangemColors2.Text.Status( status = TangemColors2.Text.Status(
@ -67,7 +81,7 @@ private fun lightThemeColors2(): TangemColors2 {
tertiary = TangemColorPalette.Dark3, tertiary = TangemColorPalette.Dark3,
quaternary = TangemColorPalette.Light4, quaternary = TangemColorPalette.Light4,
primaryInvertedConstant = TangemColorPalette.White, primaryInvertedConstant = TangemColorPalette.White,
tertiaryConstant = TangemColorPalette.Dark3, tertiaryConstant = TangemColorPalette.Dark1,
), ),
status = TangemColors2.Graphic.Status( status = TangemColors2.Graphic.Status(
accent = TangemColorPalette.Azure, accent = TangemColorPalette.Azure,
@ -243,7 +257,7 @@ private fun darkThemeColors2(): TangemColors2 {
secondary = TangemColorPalette.Light5, secondary = TangemColorPalette.Light5,
tertiary = TangemColorPalette.Dark3, tertiary = TangemColorPalette.Dark3,
quaternary = TangemColorPalette.Dark3, quaternary = TangemColorPalette.Dark3,
tertiaryConstant = TangemColorPalette.Dark3, tertiaryConstant = TangemColorPalette.Dark1,
primaryInvertedConstant = TangemColorPalette.White, primaryInvertedConstant = TangemColorPalette.White,
), ),
status = TangemColors2.Graphic.Status( status = TangemColors2.Graphic.Status(

View file

@ -1,10 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#000000" android:pathData="M4,10.535C4,9.636 4.17,8.793 4.51,8.006C4.856,7.214 5.331,6.518 5.936,5.919C6.541,5.314 7.241,4.843 8.034,4.506C8.834,4.169 9.687,4 10.594,4C11.507,4 12.361,4.169 13.154,4.506C13.954,4.843 14.656,5.314 15.261,5.919C15.866,6.518 16.338,7.214 16.678,8.006C17.024,8.793 17.197,9.636 17.197,10.535C17.197,11.241 17.086,11.913 16.865,12.55C16.649,13.187 16.349,13.767 15.966,14.292L19.66,17.969C19.773,18.076 19.857,18.202 19.911,18.346C19.97,18.491 20,18.643 20,18.804C20,19.029 19.949,19.232 19.846,19.414C19.743,19.596 19.603,19.738 19.425,19.839C19.247,19.947 19.041,20 18.809,20C18.647,20 18.49,19.971 18.339,19.912C18.193,19.858 18.061,19.772 17.942,19.655L14.224,15.97C13.705,16.312 13.138,16.583 12.523,16.781C11.912,16.973 11.269,17.07 10.594,17.07C9.687,17.07 8.834,16.901 8.034,16.564C7.241,16.227 6.541,15.759 5.936,15.159C5.331,14.56 4.856,13.866 4.51,13.08C4.17,12.288 4,11.439 4,10.535ZM5.726,10.535C5.726,11.204 5.85,11.83 6.098,12.413C6.352,12.997 6.703,13.511 7.151,13.955C7.6,14.394 8.118,14.739 8.707,14.991C9.296,15.242 9.925,15.368 10.594,15.368C11.269,15.368 11.901,15.242 12.49,14.991C13.079,14.739 13.595,14.394 14.038,13.955C14.486,13.511 14.837,12.997 15.091,12.413C15.344,11.83 15.471,11.204 15.471,10.535C15.471,9.871 15.344,9.248 15.091,8.664C14.837,8.081 14.486,7.567 14.038,7.123C13.595,6.679 13.079,6.333 12.49,6.087C11.901,5.836 11.269,5.71 10.594,5.71C9.925,5.71 9.296,5.836 8.707,6.087C8.118,6.333 7.6,6.679 7.151,7.123C6.703,7.567 6.352,8.081 6.098,8.664C5.85,9.248 5.726,9.871 5.726,10.535Z"
android:pathData="M9.5,3C11.224,3 12.877,3.685 14.096,4.904C15.315,6.123 16,7.776 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16C7.776,16 6.123,15.315 4.904,14.096C3.685,12.877 3,11.224 3,9.5C3,7.776 3.685,6.123 4.904,4.904C6.123,3.685 7.776,3 9.5,3ZM9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" /> android:fillColor="#000000"/>
</vector> </vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="20" android:viewportWidth="20" android:width="24dp">
<path android:fillColor="#919191" android:pathData="M8.666,2.333C12.164,2.333 15,5.169 15,8.666C15,10.054 14.551,11.336 13.794,12.38L17.373,15.959C17.764,16.35 17.764,16.983 17.373,17.373C16.983,17.764 16.35,17.764 15.959,17.373L12.38,13.794C11.336,14.551 10.054,15 8.666,15C5.169,15 2.333,12.164 2.333,8.666C2.333,5.169 5.169,2.333 8.666,2.333ZM8.666,4.333C6.273,4.333 4.333,6.273 4.333,8.666C4.333,11.059 6.273,13 8.666,13C11.059,13 13,11.059 13,8.666C13,6.273 11.059,4.333 8.666,4.333Z"/>
</vector>

View file

@ -1,6 +1,7 @@
package com.tangem.feature.tester.presentation.storybook.entity package com.tangem.feature.tester.presentation.storybook.entity
import com.tangem.core.ui.ds.badge.TangemBadgeColor import com.tangem.core.ui.ds.badge.TangemBadgeColor
import com.tangem.core.ui.ds.field.search.TangemFieldShape
import com.tangem.core.ui.ds.message.TangemMessageEffect import com.tangem.core.ui.ds.message.TangemMessageEffect
internal sealed interface StoryBookPage internal sealed interface StoryBookPage
@ -53,4 +54,9 @@ internal data class TangemContextMenuStory(
internal data class TangemHeaderRowStory( internal data class TangemHeaderRowStory(
val isBalanceHidden: Boolean, val isBalanceHidden: Boolean,
val onBalanceHiddenToggle: () -> Unit, val onBalanceHiddenToggle: () -> Unit,
) : StoryBookPage
internal data class TangemSearchFieldStory(
val selectedShape: TangemFieldShape,
val onShapeChange: (TangemFieldShape) -> Unit,
) : StoryBookPage ) : StoryBookPage

View file

@ -0,0 +1,18 @@
package com.tangem.feature.tester.presentation.storybook.page.searchfield
import com.tangem.core.ui.ds.field.search.TangemFieldShape
import com.tangem.feature.tester.presentation.storybook.entity.TangemSearchFieldStory
import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater
import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory
internal fun StateUpdater<TangemSearchFieldStory>.build(): TangemSearchFieldStory {
return TangemSearchFieldStory(
selectedShape = TangemFieldShape.RoundedCorners,
onShapeChange = { shape ->
updateStory { it.copy(selectedShape = shape) }
},
)
}
internal val tangemSearchFieldStoryFactory
get() = storyPageFactory(StateUpdater<TangemSearchFieldStory>::build)

View file

@ -0,0 +1,151 @@
@file:Suppress("MagicNumber", "LongMethod")
package com.tangem.feature.tester.presentation.storybook.page.searchfield
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.ds.field.search.TangemFieldShape
import com.tangem.core.ui.ds.field.search.TangemSearchField
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.tester.presentation.storybook.entity.TangemSearchFieldStory
@Composable
internal fun TangemSearchFieldStory(state: TangemSearchFieldStory, modifier: Modifier = Modifier) {
var query by remember { mutableStateOf("") }
var isActive by remember { mutableStateOf(false) }
LazyColumn(
contentPadding = PaddingValues(bottom = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.fillMaxSize()
.statusBarsPadding()
.background(TangemTheme.colors2.surface.level1),
) {
stickyHeader("shape_toggle") {
ShapeToggle(
selected = state.selectedShape,
onSelect = state.onShapeChange,
modifier = Modifier
.fillMaxWidth()
.background(TangemTheme.colors2.surface.level1)
.padding(horizontal = 16.dp, vertical = 8.dp),
)
}
item("empty") {
VariantSection(label = "Empty") {
TangemSearchField(
state = SearchBarUM(
placeholderText = resourceReference(R.string.manage_tokens_search_placeholder),
query = query,
onQueryChange = { query = it },
isActive = isActive,
onActiveChange = { isActive = it },
onClearClick = { query = "" },
),
shape = state.selectedShape,
)
}
}
item("prefilled") {
VariantSection(label = "Pre-filled") {
var prefilledQuery by remember { mutableStateOf("Bitcoin") }
var isPrefilledActive by remember { mutableStateOf(false) }
TangemSearchField(
state = SearchBarUM(
placeholderText = resourceReference(R.string.manage_tokens_search_placeholder),
query = prefilledQuery,
onQueryChange = { prefilledQuery = it },
isActive = isPrefilledActive,
onActiveChange = { isPrefilledActive = it },
onClearClick = { prefilledQuery = "" },
),
shape = state.selectedShape,
)
}
}
}
}
@Composable
private fun ShapeToggle(
selected: TangemFieldShape,
onSelect: (TangemFieldShape) -> Unit,
modifier: Modifier = Modifier,
) {
val shape = RoundedCornerShape(50)
Row(
modifier = modifier
.clip(shape)
.background(TangemTheme.colors2.surface.level2)
.border(
width = 1.dp,
color = TangemTheme.colors2.border.neutral.secondary,
shape = shape,
)
.padding(4.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
TangemFieldShape.entries.forEach { fieldShape ->
ShapeChip(
label = fieldShape.name,
selected = fieldShape == selected,
onClick = { onSelect(fieldShape) },
modifier = Modifier.weight(1f),
)
}
}
}
@Composable
private fun ShapeChip(label: String, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
val chipShape = RoundedCornerShape(50)
Box(
contentAlignment = Alignment.Center,
modifier = modifier
.clip(chipShape)
.background(
if (selected) TangemTheme.colors2.surface.level3 else TangemTheme.colors2.surface.level2,
)
.clickable(onClick = onClick)
.padding(vertical = 8.dp, horizontal = 4.dp),
) {
Text(
text = label,
style = TangemTheme.typography.caption2,
color = if (selected) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.secondary,
)
}
}
@Composable
private fun VariantSection(label: String, content: @Composable ColumnScope.() -> Unit) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
) {
Text(
text = label,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
)
content()
}
}

View file

@ -23,6 +23,7 @@ import com.tangem.feature.tester.presentation.storybook.page.contextmenu.tangemC
import com.tangem.feature.tester.presentation.storybook.page.headerrow.tangemHeaderRowStoryFactory import com.tangem.feature.tester.presentation.storybook.page.headerrow.tangemHeaderRowStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.message.tangemMessageStoryFactory import com.tangem.feature.tester.presentation.storybook.page.message.tangemMessageStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.opportunities.opportunitiesBGStoryFactory import com.tangem.feature.tester.presentation.storybook.page.opportunities.opportunitiesBGStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.searchfield.tangemSearchFieldStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.tabs.tangemSegmentedPickerStoryFactory import com.tangem.feature.tester.presentation.storybook.page.tabs.tangemSegmentedPickerStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.tokenrow.tangemTokenRowStoryFactory import com.tangem.feature.tester.presentation.storybook.page.tokenrow.tangemTokenRowStoryFactory
@ -39,6 +40,7 @@ private fun buildStories() = listOf(
StoryItem(title = "🪙 Token Row", factory = tangemTokenRowStoryFactory), StoryItem(title = "🪙 Token Row", factory = tangemTokenRowStoryFactory),
StoryItem(title = "📑 Header Row", factory = tangemHeaderRowStoryFactory), StoryItem(title = "📑 Header Row", factory = tangemHeaderRowStoryFactory),
StoryItem(title = "📋 Context Menu", factory = tangemContextMenuStoryFactory), StoryItem(title = "📋 Context Menu", factory = tangemContextMenuStoryFactory),
StoryItem(title = "🔍 Search Field", factory = tangemSearchFieldStoryFactory),
) )
@Composable @Composable

View file

@ -14,6 +14,7 @@ import com.tangem.feature.tester.presentation.storybook.entity.TangemCheckboxSto
import com.tangem.feature.tester.presentation.storybook.entity.TangemHeaderRowStory import com.tangem.feature.tester.presentation.storybook.entity.TangemHeaderRowStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemContextMenuStory import com.tangem.feature.tester.presentation.storybook.entity.TangemContextMenuStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageStory import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemSearchFieldStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemSegmentedPickerStory import com.tangem.feature.tester.presentation.storybook.entity.TangemSegmentedPickerStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowStory import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowStory
import com.tangem.feature.tester.presentation.storybook.page.background.NorthernLightsStory import com.tangem.feature.tester.presentation.storybook.page.background.NorthernLightsStory
@ -26,6 +27,7 @@ import com.tangem.feature.tester.presentation.storybook.page.tabs.TangemSegmente
import com.tangem.feature.tester.presentation.storybook.page.tokenrow.TangemTokenRowStory import com.tangem.feature.tester.presentation.storybook.page.tokenrow.TangemTokenRowStory
import com.tangem.feature.tester.presentation.storybook.page.headerrow.TangemHeaderRowStory import com.tangem.feature.tester.presentation.storybook.page.headerrow.TangemHeaderRowStory
import com.tangem.feature.tester.presentation.storybook.page.contextmenu.TangemContextMenuStory import com.tangem.feature.tester.presentation.storybook.page.contextmenu.TangemContextMenuStory
import com.tangem.feature.tester.presentation.storybook.page.searchfield.TangemSearchFieldStory
@Composable @Composable
internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier) { internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier) {
@ -48,6 +50,7 @@ internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier)
is TangemTokenRowStory -> TangemTokenRowStory(state = storyState) is TangemTokenRowStory -> TangemTokenRowStory(state = storyState)
is TangemHeaderRowStory -> TangemHeaderRowStory(state = storyState) is TangemHeaderRowStory -> TangemHeaderRowStory(state = storyState)
is TangemContextMenuStory -> TangemContextMenuStory(state = storyState) is TangemContextMenuStory -> TangemContextMenuStory(state = storyState)
is TangemSearchFieldStory -> TangemSearchFieldStory(state = storyState)
} }
} }
} }

View file

@ -38,6 +38,7 @@ import com.tangem.core.ui.ds.row.token.TangemTokenRow
import com.tangem.core.ui.ds.topbar.TangemTopBar import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.TangemTopBarActionContent import com.tangem.core.ui.ds.topbar.TangemTopBarActionContent
import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
import com.tangem.core.ui.ds.topbar.TangemTopBarType
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.reordarable.ReorderableItem import com.tangem.core.ui.reordarable.ReorderableItem
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
@ -77,6 +78,7 @@ internal fun OrganizeTokensContent(
title = { title = {
TangemTopBar( TangemTopBar(
title = resourceReference(R.string.organize_tokens_title), title = resourceReference(R.string.organize_tokens_title),
type = TangemTopBarType.BottomSheet,
endContent = { endContent = {
TangemTopBarActionContent( TangemTopBarActionContent(
actionUM = TangemTopBarActionUM( actionUM = TangemTopBarActionUM(
@ -85,7 +87,7 @@ internal fun OrganizeTokensContent(
onClick = { isShowDropdownMenu = true }, onClick = { isShowDropdownMenu = true },
ghostModeProgress = 0f, ghostModeProgress = 0f,
), ),
iconSize = TangemTheme.dimens2.x7, type = TangemTopBarType.BottomSheet,
) )
OrganizeDropDownMenu( OrganizeDropDownMenu(
organizeMenuUM = organizeTokensUM.organizeMenuUM, organizeMenuUM = organizeTokensUM.organizeMenuUM,