Removed now outdated AutocompleteTextField
This commit is contained in:
parent
40ae222dfc
commit
cd1465f144
|
@ -1,140 +0,0 @@
|
||||||
package net.codinux.banking.ui.forms
|
|
||||||
|
|
||||||
import androidx.compose.foundation.ScrollState
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material.CircularProgressIndicator
|
|
||||||
import androidx.compose.material.Divider
|
|
||||||
import androidx.compose.material.Icon
|
|
||||||
import androidx.compose.material.Text
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Close
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.focus.*
|
|
||||||
import androidx.compose.ui.geometry.Size
|
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.toSize
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import net.codinux.banking.ui.config.Colors
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun <T> AutocompleteTextField(
|
|
||||||
onValueChange: (T?) -> Unit,
|
|
||||||
modifier: Modifier = Modifier.fillMaxWidth(),
|
|
||||||
label: @Composable () -> Unit = { Text("Search") },
|
|
||||||
showDividersBetweenItems: Boolean = true,
|
|
||||||
getItemTitle: ((T) -> String)? = null,
|
|
||||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
|
||||||
fetchSuggestions: suspend (query: String) -> List<T> = { emptyList() },
|
|
||||||
suggestionContent: @Composable (T) -> Unit
|
|
||||||
) {
|
|
||||||
var searchQuery by remember { mutableStateOf("") }
|
|
||||||
var isLoading by remember { mutableStateOf(false) }
|
|
||||||
var suggestions by remember { mutableStateOf<List<T>>(emptyList()) }
|
|
||||||
var textFieldSize by remember { mutableStateOf(Size.Zero) }
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
val textFieldFocus = remember { FocusRequester() }
|
|
||||||
val focusManager = LocalFocusManager.current
|
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
|
|
||||||
Box(Modifier.fillMaxWidth()) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = searchQuery,
|
|
||||||
onValueChange = { query ->
|
|
||||||
searchQuery = query
|
|
||||||
onValueChange(null)
|
|
||||||
|
|
||||||
if (query.length >= 1) {
|
|
||||||
isLoading = true
|
|
||||||
|
|
||||||
coroutineScope.launch {
|
|
||||||
suggestions = fetchSuggestions(query)
|
|
||||||
isLoading = false
|
|
||||||
expanded = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
suggestions = emptyList()
|
|
||||||
expanded = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label = label,
|
|
||||||
modifier = modifier
|
|
||||||
.focusRequester(textFieldFocus)
|
|
||||||
.onGloballyPositioned {
|
|
||||||
textFieldSize = it.size.toSize()
|
|
||||||
},
|
|
||||||
keyboardOptions = keyboardOptions,
|
|
||||||
trailingIcon = {
|
|
||||||
if (isLoading) {
|
|
||||||
CircularProgressIndicator(
|
|
||||||
modifier = Modifier.size(24.dp),
|
|
||||||
strokeWidth = 2.dp
|
|
||||||
)
|
|
||||||
} else if (searchQuery.isNotEmpty()) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Close,
|
|
||||||
contentDescription = "Clear",
|
|
||||||
modifier = Modifier.clickable {
|
|
||||||
searchQuery = ""
|
|
||||||
suggestions = emptyList()
|
|
||||||
expanded = false
|
|
||||||
onValueChange(null)
|
|
||||||
textFieldFocus.requestFocus()
|
|
||||||
}.focusProperties { canFocus = false }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// isError = selectedItem == null && searchQuery.isNotEmpty()
|
|
||||||
)
|
|
||||||
|
|
||||||
if (expanded) {
|
|
||||||
RoundedCornersCard(
|
|
||||||
Modifier.width(textFieldSize.width.dp)
|
|
||||||
.heightIn(max = 400.dp)
|
|
||||||
.padding(top = with(LocalDensity.current) { (textFieldSize.height + 2).toDp() })
|
|
||||||
,
|
|
||||||
cornerSize = 4.dp,
|
|
||||||
shadowElevation = 4.dp
|
|
||||||
) {
|
|
||||||
Column(Modifier.verticalScroll(ScrollState(0), enabled = true)) {
|
|
||||||
suggestions.forEachIndexed { index, item ->
|
|
||||||
Column(
|
|
||||||
Modifier.fillMaxWidth().padding(8.dp).clickable {
|
|
||||||
onValueChange(item)
|
|
||||||
getItemTitle?.let {
|
|
||||||
searchQuery = it.invoke(item)
|
|
||||||
}
|
|
||||||
expanded = false
|
|
||||||
|
|
||||||
textFieldFocus.freeFocus()
|
|
||||||
focusManager.moveFocus(FocusDirection.Down)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
suggestionContent(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showDividersBetweenItems && index < suggestions.size - 1) {
|
|
||||||
Divider(color = Colors.Zinc200, thickness = 1.dp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LaunchedEffect(textFieldFocus) {
|
|
||||||
textFieldFocus.requestFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -6,8 +6,7 @@ import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.LocalTextStyle
|
import androidx.compose.material.LocalTextStyle
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.TextFieldDefaults
|
import androidx.compose.material.TextFieldDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
@ -15,7 +14,11 @@ import androidx.compose.ui.input.key.*
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.VisualTransformation
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import net.codinux.banking.ui.config.Colors
|
import net.codinux.banking.ui.config.Colors
|
||||||
|
import net.codinux.banking.ui.config.DI
|
||||||
|
|
||||||
|
private val uiService = DI.uiService
|
||||||
|
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -47,12 +50,20 @@ fun OutlinedTextField(
|
||||||
focusedLabelColor = Colors.CodinuxSecondaryColor // does not work
|
focusedLabelColor = Colors.CodinuxSecondaryColor // does not work
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var debounceJob by remember { mutableStateOf<Job?>(null) }
|
||||||
|
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
androidx.compose.material.OutlinedTextField(
|
androidx.compose.material.OutlinedTextField(
|
||||||
value = value,
|
value = value,
|
||||||
onValueChange = onValueChange,
|
onValueChange = onValueChange,
|
||||||
modifier = modifier.onKeyEvent { event -> // onKeyEvent only handle input on hardware keyboards
|
modifier = modifier.onKeyEvent { event -> // onKeyEvent only handle input on hardware keyboards
|
||||||
if (onEnterPressed != null && event.type == KeyEventType.KeyUp && (event.key == Key.Enter || event.key == Key.NumPadEnter)) {
|
if (onEnterPressed != null && event.type == KeyEventType.KeyUp && (event.key == Key.Enter || event.key == Key.NumPadEnter)) {
|
||||||
|
debounceJob?.cancel()
|
||||||
|
debounceJob = uiService.debounce(coroutineScope) {
|
||||||
onEnterPressed()
|
onEnterPressed()
|
||||||
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
@ -17,7 +17,7 @@ import bankmeister.composeapp.generated.resources.visibility
|
||||||
import bankmeister.composeapp.generated.resources.visibility_off
|
import bankmeister.composeapp.generated.resources.visibility_off
|
||||||
import org.jetbrains.compose.resources.imageResource
|
import org.jetbrains.compose.resources.imageResource
|
||||||
|
|
||||||
@Composable
|
@Composable // try BasicSecureTextField
|
||||||
fun PasswordTextField(password: String = "", label: String = "Passwort", forceHidePassword: Boolean? = null, onEnterPressed: (() -> Unit)? = null, onChange: (String) -> Unit) {
|
fun PasswordTextField(password: String = "", label: String = "Passwort", forceHidePassword: Boolean? = null, onEnterPressed: (() -> Unit)? = null, onChange: (String) -> Unit) {
|
||||||
|
|
||||||
var passwordVisible by remember { mutableStateOf(false) }
|
var passwordVisible by remember { mutableStateOf(false) }
|
||||||
|
|
Loading…
Reference in New Issue