diff --git a/app/src/main/java/com/tangem/tap/common/compose/ErrorViews.kt b/app/src/main/java/com/tangem/tap/common/compose/ErrorViews.kt new file mode 100644 index 0000000000..7055706474 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/compose/ErrorViews.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.common.compose + +import androidx.compose.material.LocalTextStyle +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle + +/** +[REDACTED_AUTHOR] + */ +@Composable +fun ErrorView( + text: String, + modifier: Modifier = Modifier, + style: TextStyle = LocalTextStyle.current +) { + Text( + text, + color = MaterialTheme.colors.error, + modifier = modifier, + style = style + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt b/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt index 44f5ab1634..65429321eb 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt @@ -1,13 +1,18 @@ package com.tangem.tap.common.compose import androidx.compose.animation.* +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material.* +import androidx.compose.material.LinearProgressIndicator +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Scaffold +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -31,42 +36,91 @@ fun OutlinedTextFieldWidget( label: String = "", placeholderId: Int? = null, placeholder: String = "", + trailingIcon: @Composable (() -> Unit)? = null, isEnabled: Boolean = true, + isVisible: Boolean = true, + isLoading: Boolean = false, error: DomainError? = null, errorConverter: ErrorConverter? = null, debounceTextChanges: Long = 400, visualTransformation: VisualTransformation = VisualTransformation.None, onTextChanged: (String) -> Unit, ) { + if (!isVisible) return + val placeholder = placeholderId?.let { stringResource(id = it) } ?: placeholder val label = labelId?.let { stringResource(id = it) } ?: label - val rTextValue = remember { mutableStateOf(text) } - val textDebouncer = ComposableTextDebouncer(text, debounceTextChanges, onTextChanged) - Column( modifier = modifier.animateContentSize(), ) { - OutlinedTextField( - value = rTextValue.value, - onValueChange = { - rTextValue.value = it - textDebouncer.emmit(it) - }, - modifier = Modifier.fillMaxWidth(), - label = { Text(label) }, - placeholder = { Text(placeholder) }, - singleLine = true, - enabled = isEnabled, - isError = error != null, + OutlinedProgressTextField( + text = text, + modifier = modifier, + label = label, + placeholder = placeholder, + trailingIcon = trailingIcon, + isEnabled = isEnabled, + isLoading = isLoading, + error = error, + debounceTextChanges = debounceTextChanges, visualTransformation = visualTransformation, + onTextChanged = onTextChanged ) errorConverter?.let { TextFieldErrorWidget(error, it) } } } @Composable -fun TextFieldErrorWidget( +private fun OutlinedProgressTextField( + text: String, + modifier: Modifier = Modifier, + label: String = "", + placeholder: String = "", + isEnabled: Boolean = true, + isLoading: Boolean = false, + error: DomainError? = null, + debounceTextChanges: Long = 400, + visualTransformation: VisualTransformation = VisualTransformation.None, + trailingIcon: @Composable (() -> Unit)? = null, + onTextChanged: (String) -> Unit, +) { + val rTextValue = remember { mutableStateOf(text) } + val textDebouncer = ComposableTextDebouncer(text, debounceTextChanges, onTextChanged) + + // add ability to paste text from state + if (rTextValue.value != text) rTextValue.value = text + + Box { + OutlinedTextField( + value = rTextValue.value, + onValueChange = { + // immediately change text for the OutlinedTextField + rTextValue.value = it + textDebouncer.emmit(it) + }, + modifier = Modifier.fillMaxWidth(), + label = { Text(label) }, + placeholder = { Text(placeholder) }, + trailingIcon = trailingIcon, + singleLine = true, + enabled = isEnabled, + isError = error != null, + visualTransformation = visualTransformation, + ) + AnimatedVisibility( + modifier = modifier + .fillMaxWidth() + .align(Alignment.BottomCenter) + .padding(start = 6.dp, top = 0.dp, end = 6.dp, bottom = 6.dp), + visible = isLoading, + ) { LinearProgressIndicator() } + } + +} + +@Composable +private fun TextFieldErrorWidget( error: DomainError? = null, errorConverter: ErrorConverter, ) { @@ -75,30 +129,12 @@ fun TextFieldErrorWidget( enter = fadeIn() + slideInVertically(), exit = slideOutVertically() + fadeOut(), ) { - ErrorView( - errorConverter.convertError(error!!), - style = TextStyle( - fontSize = 14.sp - ) - ) + error?.let { + ErrorView(errorConverter.convertError(it), style = TextStyle(fontSize = 14.sp)) + } } } -@Composable -fun ErrorView( - text: String, - modifier: Modifier = Modifier, - style: TextStyle = LocalTextStyle.current -) { - Text( - text, - color = MaterialTheme.colors.error, - modifier = modifier, - style = style - ) -} - - @Preview @Composable fun OutlinedTextFieldWithErrorTest() { @@ -142,6 +178,16 @@ fun OutlinedTextFieldWithErrorTest() { errorConverter = converter, onTextChanged = {}, ) + OutlinedTextFieldWidget( + modifier = modifier, + text = "First", + label = "First label", + placeholder = "1 placeholder", + isLoading = true, + error = null, + errorConverter = converter, + onTextChanged = {}, + ) OutlinedTextFieldWidget( modifier = modifier, text = "First",