Last active
September 7, 2022 20:42
-
-
Save yoloroy/a24a8455e8bee059d77afb2a8595ac22 to your computer and use it in GitHub Desktop.
ListInputComponent
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package components | |
| import androidx.compose.desktop.ui.tooling.preview.Preview | |
| import androidx.compose.foundation.clickable | |
| import androidx.compose.foundation.layout.* | |
| import androidx.compose.foundation.lazy.LazyRow | |
| import androidx.compose.foundation.lazy.itemsIndexed | |
| import androidx.compose.material.Icon | |
| import androidx.compose.material.IconButton | |
| import androidx.compose.material.OutlinedTextField | |
| import androidx.compose.material.Text | |
| import androidx.compose.material.icons.Icons | |
| import androidx.compose.material.icons.filled.AddCircle | |
| import androidx.compose.material.icons.filled.Close | |
| import androidx.compose.runtime.* | |
| import androidx.compose.ui.Alignment | |
| import androidx.compose.ui.Modifier | |
| import androidx.compose.ui.unit.Dp | |
| import androidx.compose.ui.unit.dp | |
| @Composable | |
| fun <T : Presentable> ListInput(stateHolder: ListInputStateHolder<T>) { | |
| val items = stateHolder.state.presentationList | |
| val errorMessage = stateHolder.state.errorText | |
| var currentEditItemIndex: Int by remember { mutableStateOf(-1) } | |
| var currentEditedText: String by remember { mutableStateOf("null") } | |
| fun onEditItem(index: Int, text: String): () -> Unit = { | |
| currentEditItemIndex = index | |
| currentEditedText = text | |
| } | |
| fun onStopEditing(index: Int): () -> Unit = { | |
| currentEditItemIndex = -1 | |
| if (currentEditedText.isBlank()) stateHolder.remove(index) | |
| else stateHolder.update(index, currentEditedText) | |
| } | |
| val onAppend: () -> Unit = { | |
| currentEditItemIndex = items.size | |
| currentEditedText = stateHolder.append() | |
| } | |
| Column { | |
| errorMessage?.let { Text(it) } | |
| Spacer(modifier = Modifier.height(12.dp)) | |
| LazyRow( | |
| modifier = Modifier.fillMaxWidth(), | |
| horizontalArrangement = Arrangement.Start, | |
| verticalAlignment = Alignment.CenterVertically | |
| ) { | |
| itemsIndexed(items) { index, text -> | |
| if (index == currentEditItemIndex) { | |
| OutlinedTextField( | |
| value = currentEditedText, | |
| onValueChange = { currentEditedText = it }, | |
| modifier = Modifier.widthIn(1.dp, Dp.Infinity), | |
| trailingIcon = { | |
| IconButton(onClick = onStopEditing(index)) { | |
| Icon(Icons.Default.Close, "Cancel") | |
| } | |
| } | |
| ) | |
| } else { | |
| Text( | |
| text = text, | |
| modifier = Modifier | |
| .padding(vertical = 8.dp, horizontal = 4.dp) | |
| .clickable(onClick = onEditItem(index, text)) | |
| ) | |
| } | |
| } | |
| item { | |
| IconButton(onClick = onAppend) { | |
| Icon(Icons.Default.AddCircle, "Append") | |
| } | |
| } | |
| } | |
| } | |
| } | |
| @Composable | |
| @Preview | |
| fun ListInputPreview() { | |
| val presentables = "hello".map { c -> Presentable.String(c.toString()) } | |
| val stateHolder = ListInputStateHolder(presentables, Presentable.String.Creator, "Bad input") | |
| Box( | |
| modifier = Modifier.size(400.dp), | |
| contentAlignment = Alignment.Center | |
| ) { | |
| ListInput(stateHolder) | |
| } | |
| } | |
| class ListInputStateHolder<T : Presentable>( | |
| initial: List<T>, | |
| private val creator: Presentable.Creator<T>, | |
| private val errorTextBadValue: String | |
| ) { | |
| var state: ListInputState<T> by mutableStateOf(ListInputState(initial)) | |
| private set | |
| /** | |
| * @return [creator].defaultValue | |
| */ | |
| fun append(): String { | |
| val value = creator.defaultValue | |
| state = state.copy(list = state.list + value, errorText = null) | |
| return value.toText() | |
| } | |
| fun remove(index: Int) { | |
| val previousValues = state.list.take(index) | |
| val postValues = state.list.takeLast(state.list.size - index - 1) | |
| state = state.copy(list = previousValues + postValues, errorText = null) | |
| } | |
| fun update(index: Int, stringValue: String) { | |
| val value = creator.valueOfOrNull(stringValue) | |
| if (value == null) { | |
| state = state.copy(errorText = errorTextBadValue) | |
| return | |
| } | |
| val previousValues = state.list.take(index) | |
| val postValues = state.list.takeLast(state.list.size - index - 1) | |
| state = state.copy(list = previousValues + value + postValues, errorText = null) | |
| } | |
| } | |
| data class ListInputState<T : Presentable>( | |
| val list: List<T>, | |
| val errorText: String? = null | |
| ) { | |
| val presentationList: List<String> get() = list.map { it.toText() } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import androidx.compose.desktop.ui.tooling.preview.Preview | |
| import androidx.compose.material.MaterialTheme | |
| import androidx.compose.runtime.Composable | |
| import androidx.compose.ui.window.Window | |
| import androidx.compose.ui.window.application | |
| import components.ListInput | |
| import components.ListInputStateHolder | |
| import components.Presentable | |
| @Composable | |
| @Preview | |
| fun App() { | |
| val presentables = listOf(1, 2, 3, 4, 5).map { Presentable.Int(it) } | |
| val stateHolder = ListInputStateHolder(presentables, Presentable.Int.Creator, "Bad input") | |
| MaterialTheme { | |
| ListInput(stateHolder) | |
| } | |
| } | |
| fun main() = application { | |
| Window(onCloseRequest = ::exitApplication) { | |
| App() | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package components | |
| interface Presentable { | |
| fun toText(): kotlin.String | |
| interface Creator<T : Presentable> { | |
| val defaultValue: T | |
| fun valueOfOrNull(string: kotlin.String): T? | |
| } | |
| class Int(val int: kotlin.Int) : Presentable { | |
| override fun toText() = int.toString() | |
| object Creator : Presentable.Creator<Int> { | |
| override val defaultValue: Int get() = Int(0) | |
| override fun valueOfOrNull(string: kotlin.String) = string.toIntOrNull()?.let { Int(it) } | |
| } | |
| } | |
| class String(val string: kotlin.String) : Presentable { | |
| override fun toText() = string | |
| object Creator : Presentable.Creator<String> { | |
| override val defaultValue: String get() = String("+") | |
| override fun valueOfOrNull(string: kotlin.String) = String(string) | |
| } | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
2022-09-07.23.01.05.mov