Updated on 2026-08-14
This commit is contained in:
parent
d1798a2c49
commit
cf4affa513
16 changed files with 572 additions and 22 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
10
core/ui/src/main/res/drawable/ic_search_24.xml
Normal file
10
core/ui/src/main/res/drawable/ic_search_24.xml
Normal 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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue