Updated on 2026-08-14

This commit is contained in:
Tangem 2022-12-26 11:27:09 +04:00
parent d1798a2c49
commit cf4affa513
16 changed files with 572 additions and 22 deletions

View file

@ -204,6 +204,9 @@
<string name="swapping_insufficient_funds">Insufficient funds</string>
<string name="swapping_give_permission">Give Permission</string>
<string name="swapping_permit_and_swap">Permit and Swap</string>
<string name="swapping_token_list_your_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>

View file

@ -204,6 +204,9 @@
<string name="swapping_insufficient_funds">Insufficient funds</string>
<string name="swapping_give_permission">Give Permission</string>
<string name="swapping_permit_and_swap">Permit and Swap</string>
<string name="swapping_token_list_your_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>

View file

@ -204,6 +204,9 @@
<string name="swapping_insufficient_funds">Insufficient funds</string>
<string name="swapping_give_permission">Give Permission</string>
<string name="swapping_permit_and_swap">Permit and Swap</string>
<string name="swapping_token_list_your_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>

View file

@ -204,6 +204,9 @@
<string name="swapping_insufficient_funds">Недостаточно средств</string>
<string name="swapping_give_permission">Дать разрешение</string>
<string name="swapping_permit_and_swap">Разрешить и обменять</string>
<string name="swapping_token_list_your_title">Выберите токен</string>
<string name="swapping_token_list_your_tokens">Ваши токены</string>
<string name="swapping_token_list_other_tokens">Другие токены</string>
<string name="referral_error_failed_to_load_info">Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже.</string>
<string name="referral_error_failed_to_load_info_with_reason">Не удалось загрузить информацию по реферальной программе. Причина: %s. Пожалуйста, попробуйте позже.</string>
<string name="referral_error_failed_to_participate">Не удалось обработать вашу заявку на участие. Причина: %s. Пожалуйста, попробуйте позже. Если проблема сохранится, вы можете обратиться в техподдержку.</string>

View file

@ -204,6 +204,9 @@
<string name="swapping_insufficient_funds">Insufficient funds</string>
<string name="swapping_give_permission">Give Permission</string>
<string name="swapping_permit_and_swap">Permit and Swap</string>
<string name="swapping_token_list_your_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>

View file

@ -100,6 +100,11 @@ fun SpacerW(width: Dp, modifier: Modifier = Modifier) {
Spacer(modifier = modifier.width(width))
}
@Composable
fun SpacerW2(modifier: Modifier = Modifier) {
SpacerW(2.dp, modifier)
}
@Composable
fun SpacerW4(modifier: Modifier = Modifier) {
SpacerW(4.dp, modifier)

View file

@ -192,7 +192,7 @@ private fun TangemTextFieldSize.toShape(): Shape = when (this) {
TangemTextFieldSize.Default -> TangemTheme.shapes.roundedCornersSmall2
}
private object TangemTextFieldsDefault {
internal object TangemTextFieldsDefault {
val defaultTextFieldColors: TangemTextFieldColors
@Composable @Stable get() = TangemTextFieldColors(
textColor = TangemTheme.colors.text.primary1,
@ -223,7 +223,7 @@ private object TangemTextFieldsDefault {
}
@Immutable
private data class TangemTextFieldColors(
internal data class TangemTextFieldColors(
private val textColor: Color,
private val disabledTextColor: Color,
private val cursorColor: Color,

View file

@ -0,0 +1,245 @@
package com.tangem.core.ui.components.appbar
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.TangemTextFieldsDefault
import com.tangem.core.ui.res.TangemTheme
/**
* App bar with close icon and search functionality
*
* @param title optional title
* @param placeholderSearchText optional text placeholder for search TextField
* @param expandedInitially whether the search is expanded on launch
* @param tint tint for most of the visual elements of toolbar
* @param onBackClick action when close button is clicked
* @param onSearchChanged action when search is modified
* @param onSearchDisplayClosed action when search is closed
*
* @see <a href = "https://www.figma.com/file/Vs6SkVsFnUPsSCNwlnVf5U/Android-%E2%80%93-UI?node-id=1123%3A4068&t=xj8BBj5DfCWn2Mli-1"
* >Figma component</a>
*/
@Composable
fun ExpandableSearchView(
modifier: Modifier = Modifier,
title: String? = null,
placeholderSearchText: String = "",
expandedInitially: Boolean = false,
tint: Color = TangemTheme.colors.text.primary1,
onBackClick: () -> Unit,
onSearchChanged: (String) -> Unit,
onSearchDisplayClosed: () -> Unit,
) {
val (expanded, onExpandedChanged) = remember {
mutableStateOf(expandedInitially)
}
Crossfade(targetState = expanded) { isSearchFieldVisible ->
if (isSearchFieldVisible) {
ExpandedSearchView(
placeholderSearchText = placeholderSearchText,
onSearchChanged = onSearchChanged,
onSearchDisplayClosed = onSearchDisplayClosed,
onExpandedChanged = onExpandedChanged,
modifier = modifier,
tint = tint,
)
} else {
CollapsedSearchView(
title = title,
onBackClick = onBackClick,
onExpandedChanged = onExpandedChanged,
modifier = modifier,
tint = tint,
)
}
}
}
@Composable
private fun SearchIcon(iconTint: Color) {
Icon(
painter = painterResource(id = R.drawable.ic_search_24),
contentDescription = null,
tint = iconTint,
)
}
@Composable
private fun CollapsedSearchView(
modifier: Modifier = Modifier,
title: String? = null,
onBackClick: () -> Unit,
onExpandedChanged: (Boolean) -> Unit,
tint: Color = TangemTheme.colors.background.primary,
) {
Row(
modifier = modifier
.background(TangemTheme.colors.background.primary)
.padding(TangemTheme.dimens.spacing16)
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painter = painterResource(R.drawable.ic_close_24),
contentDescription = null,
modifier = Modifier
.size(TangemTheme.dimens.size24)
.clickable { onBackClick() },
tint = MaterialTheme.colors.onPrimary,
)
if (!title.isNullOrBlank()) {
Text(
text = title,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
style = TangemTheme.typography.subtitle1,
)
}
SpacerWMax()
Icon(
painter = painterResource(id = R.drawable.ic_search_24),
tint = tint,
contentDescription = null,
modifier = Modifier
.clickable { onExpandedChanged(true) },
)
}
}
@Composable
private fun ExpandedSearchView(
placeholderSearchText: String,
onSearchChanged: (String) -> Unit,
onSearchDisplayClosed: () -> Unit,
onExpandedChanged: (Boolean) -> Unit,
modifier: Modifier = Modifier,
tint: Color = TangemTheme.colors.background.primary,
) {
val focusManager = LocalFocusManager.current
val textFieldFocusRequester = remember { FocusRequester() }
SideEffect {
textFieldFocusRequester.requestFocus()
}
var textFieldValue by remember {
mutableStateOf(TextFieldValue("", TextRange("".length)))
}
Row(
modifier = modifier
.background(TangemTheme.colors.background.primary)
.fillMaxWidth(),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(
onClick = {
onExpandedChanged(false)
onSearchDisplayClosed()
},
) {
Icon(
painter = painterResource(id = R.drawable.ic_back_24),
contentDescription = null,
tint = tint,
)
}
TextField(
value = textFieldValue,
onValueChange = {
textFieldValue = it
onSearchChanged(it.text)
},
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.focusRequester(textFieldFocusRequester),
placeholder = {
Text(text = placeholderSearchText)
},
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(
onDone = {
focusManager.clearFocus()
},
),
colors = TangemTextFieldsDefault.defaultTextFieldColors
.copy(
placeholderColor = TangemTheme.colors.text.disabled,
cursorColor = TangemTheme.colors.text.tertiary,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
),
)
}
}
@Preview
@Composable
fun CollapsedSearchViewPreview() {
TangemTheme {
ExpandableSearchView(
title = "Choose Token",
onBackClick = {},
placeholderSearchText = "Search",
onSearchChanged = {},
onSearchDisplayClosed = {},
)
}
}
@Preview
@Composable
fun ExpandedSearchViewPreview() {
TangemTheme {
ExpandableSearchView(
title = "Choose Token",
onBackClick = {},
placeholderSearchText = "Search",
onSearchChanged = {},
expandedInitially = true,
onSearchDisplayClosed = {},
)
}
}

View file

@ -65,6 +65,7 @@ data class TangemDimens internal constructor(
val spacing0_5: Dp = 0.5.dp,
val spacing2: Dp = 2.dp,
val spacing4: Dp = 4.dp,
val spacing6: Dp = 6.dp,
val spacing8: Dp = 8.dp,
val spacing10: Dp = 10.dp,
val spacing12: Dp = 12.dp,
@ -77,6 +78,7 @@ data class TangemDimens internal constructor(
val spacing34: Dp = 34.dp,
val spacing44: Dp = 44.dp,
val spacing50: Dp = 50.dp,
val spacing52: Dp = 52.dp,
val spacing54: Dp = 54.dp,
val spacing56: Dp = 56.dp,
val spacing92: Dp = 92.dp,

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
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" />
</vector>