From a293ea0f2b5d3975c7c372b5f2ebe3a6c54fcd5a Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 8 Nov 2024 15:37:10 +0100 Subject: [PATCH] design : TextField2 is now TextField --- .../impl/configureroom/ConfigureRoomView.kt | 8 +- .../logout/impl/AccountDeactivationView.kt | 4 +- .../features/joinroom/impl/JoinRoomView.kt | 4 +- .../loginpassword/LoginPasswordView.kt | 6 +- .../SearchAccountProviderView.kt | 4 +- .../messages/impl/report/ReportMessageView.kt | 4 +- .../poll/impl/create/CreatePollView.kt | 6 +- .../user/editprofile/EditUserProfileView.kt | 4 +- .../rageshake/impl/bugreport/BugReportView.kt | 4 +- .../impl/edit/RoomDetailsEditView.kt | 6 +- .../impl/root/RoomDirectoryView.kt | 4 +- .../impl/search/RoomListSearchView.kt | 4 +- .../password/ResetIdentityPasswordView.kt | 4 +- .../impl/setup/views/RecoveryKeyView.kt | 4 +- .../theme/components/FilledTextField.kt | 202 +++++++++++ .../theme/components/TextField.kt | 337 ++++++++++++------ .../theme/components/TextField2.kt | 317 ---------------- 17 files changed, 456 insertions(+), 466 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FilledTextField.kt delete mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField2.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index b9528027c4..a80f9890c3 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -46,7 +46,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text @@ -216,7 +216,7 @@ private fun RoomNameWithAvatar( modifier = Modifier.clickable(onClick = onAvatarClick), ) - TextField2( + TextField( label = stringResource(R.string.screen_create_room_room_name_label), value = roomName, placeholder = stringResource(CommonStrings.common_room_name_placeholder), @@ -232,7 +232,7 @@ private fun RoomTopic( onTopicChange: (String) -> Unit, modifier: Modifier = Modifier, ) { - TextField2( + TextField( modifier = modifier, label = stringResource(R.string.screen_create_room_topic_label), value = topic, @@ -322,7 +322,7 @@ private fun RoomAddressField( modifier: Modifier = Modifier, ) { - TextField2( + TextField( modifier = modifier.fillMaxWidth(), value = address.value, label = stringResource(R.string.screen_create_room_room_address_section_title), diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt index c95a6934f0..16d53a67a8 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt @@ -64,7 +64,7 @@ import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag @@ -268,7 +268,7 @@ private fun Content( // Ensure password is hidden when user submits the form passwordVisible = false } - TextField2( + TextField( value = passwordFieldState, label = stringResource(CommonStrings.action_confirm_password), readOnly = isLoading, diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index e6cf8d23e7..3a1c7c8421 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -61,7 +61,7 @@ import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.room.RoomType @@ -390,7 +390,7 @@ private fun DefaultLoadedContent( ) } else if (contentState.joinAuthorisationStatus is JoinAuthorisationStatus.CanKnock) { Spacer(modifier = Modifier.height(24.dp)) - TextField2( + TextField( value = knockMessage, onValueChange = onKnockMessageUpdate, maxLines = 3, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index df4341d374..bc4ea74773 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -60,7 +60,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Scaffold -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag @@ -169,7 +169,7 @@ private fun LoginForm( val eventSink = state.eventSink Column { - TextField2( + TextField( label = stringResource(R.string.screen_login_form_header), value = loginFieldState, enabled = !isLoading, @@ -221,7 +221,7 @@ private fun LoginForm( passwordVisible = false } Spacer(Modifier.height(20.dp)) - TextField2( + TextField( value = passwordFieldState, enabled = !isLoading, modifier = Modifier diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt index 63131d6845..2bc6f664a3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt @@ -56,7 +56,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @@ -102,7 +102,7 @@ fun SearchAccountProviderView( // TextInput var userInputState by textFieldState(stateValue = state.userInput) val focusManager = LocalFocusManager.current - TextField2( + TextField( value = userInputState, // readOnly = isLoading, modifier = Modifier diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt index b96bc3303e..38e441c26a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt @@ -40,7 +40,7 @@ import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.ui.strings.CommonStrings @@ -88,7 +88,7 @@ fun ReportMessageView( ) { Spacer(modifier = Modifier.height(20.dp)) - TextField2( + TextField( value = state.reason, onValueChange = { state.eventSink(ReportMessageEvents.UpdateReason(it)) }, placeholder = stringResource(R.string.screen_report_content_hint), diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt index 1b3c1a10fb..00f57d25d8 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt @@ -50,7 +50,7 @@ import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.ui.strings.CommonStrings @@ -110,7 +110,7 @@ fun CreatePollView( Column { ListItem( headlineContent = { - TextField2( + TextField( label = stringResource(id = R.string.screen_create_poll_question_desc), value = state.question, onValueChange = { @@ -130,7 +130,7 @@ fun CreatePollView( val isLastItem = index == state.answers.size - 1 ListItem( headlineContent = { - TextField2( + TextField( value = answer.text, onValueChange = { state.eventSink(CreatePollEvents.SetAnswer(index, it)) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt index 3457d62be7..078469fd34 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt @@ -40,7 +40,7 @@ import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet import io.element.android.libraries.matrix.ui.components.EditableAvatarView @@ -112,7 +112,7 @@ fun EditUserProfileView( textAlign = TextAlign.Center, ) Spacer(modifier = Modifier.height(40.dp)) - TextField2( + TextField( label = stringResource(R.string.screen_edit_profile_display_name), value = state.displayName, placeholder = stringResource(CommonStrings.common_room_name_placeholder), diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index b7a37d670b..65fd57c88c 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -44,7 +44,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground import io.element.android.libraries.designsystem.theme.components.Button -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -69,7 +69,7 @@ fun BugReportView( ) Spacer(modifier = Modifier.height(16.dp)) PreferenceRow { - TextField2( + TextField( value = descriptionFieldState, modifier = Modifier .fillMaxWidth() diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index 1af87818ff..a56af7f5f4 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -42,7 +42,7 @@ import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet import io.element.android.libraries.matrix.ui.components.EditableAvatarView @@ -108,7 +108,7 @@ fun RoomDetailsEditView( ) Spacer(modifier = Modifier.height(60.dp)) - TextField2( + TextField( label = stringResource(id = R.string.screen_room_details_room_name_label), value = state.roomRawName, placeholder = stringResource(CommonStrings.common_room_name_placeholder), @@ -119,7 +119,7 @@ fun RoomDetailsEditView( Spacer(modifier = Modifier.height(28.dp)) - TextField2( + TextField( label = stringResource(CommonStrings.common_topic), value = state.roomTopic, placeholder = stringResource(CommonStrings.common_topic_placeholder), diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt index 0402a276fc..12b6c30184 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt @@ -49,7 +49,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TextField +import io.element.android.libraries.designsystem.theme.components.FilledTextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.ui.strings.CommonStrings @@ -194,7 +194,7 @@ private fun SearchTextField( ), ) { val focusManager = LocalFocusManager.current - TextField( + FilledTextField( modifier = modifier.testTag(TestTags.searchTextField.value), textStyle = ElementTheme.typography.fontBodyLgRegular, singleLine = true, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt index 73f00330b6..5b8ce3bd24 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt @@ -53,7 +53,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TextField +import io.element.android.libraries.designsystem.theme.components.FilledTextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.copy import io.element.android.libraries.matrix.api.core.RoomId @@ -130,7 +130,7 @@ private fun RoomListSearchContent( title = { val filter = state.query val focusRequester = FocusRequester() - TextField( + FilledTextField( modifier = Modifier .fillMaxWidth() .focusRequester(focusRequester), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt index 3ae77f607e..6b0c2036a1 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt @@ -32,7 +32,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -80,7 +80,7 @@ fun ResetIdentityPasswordView( @Composable private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Boolean) { var showPassword by remember { mutableStateOf(false) } - TextField2( + TextField( modifier = Modifier .fillMaxWidth() .onTabOrEnterKeyFocusNext(LocalFocusManager.current), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt index f6995dd096..bbbea8e6c5 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt @@ -43,7 +43,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TextField2 +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @@ -158,7 +158,7 @@ private fun RecoveryKeyFormContent( // Do not apply a visual transformation if the key has spaces, to let user enter passphrase if (keyHasSpace) VisualTransformation.None else RecoveryKeyVisualTransformation() } - TextField2( + TextField( modifier = Modifier .fillMaxWidth() .testTag(TestTags.recoveryKey) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FilledTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FilledTextField.kt new file mode 100644 index 0000000000..1055bea107 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FilledTextField.kt @@ -0,0 +1,202 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.designsystem.theme.components + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.TextFieldColors +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.designsystem.utils.allBooleans +import io.element.android.libraries.designsystem.utils.asInt + +@Composable +fun FilledTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + readOnly: Boolean = false, + textStyle: TextStyle = LocalTextStyle.current, + label: @Composable (() -> Unit)? = null, + placeholder: @Composable (() -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + supportingText: @Composable (() -> Unit)? = null, + isError: Boolean = false, + visualTransformation: VisualTransformation = VisualTransformation.None, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false, + maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + shape: Shape = TextFieldDefaults.shape, + colors: TextFieldColors = TextFieldDefaults.colors( + unfocusedContainerColor = ElementTheme.colors.bgSubtleSecondary, + focusedContainerColor = ElementTheme.colors.bgSubtleSecondary, + disabledContainerColor = ElementTheme.colors.bgSubtleSecondary, + errorContainerColor = ElementTheme.colors.bgSubtleSecondary, + ) +) { + androidx.compose.material3.TextField( + value = value, + onValueChange = onValueChange, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + textStyle = textStyle, + label = label, + placeholder = placeholder, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + supportingText = supportingText, + isError = isError, + visualTransformation = visualTransformation, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + maxLines = maxLines, + interactionSource = interactionSource, + shape = shape, + colors = colors, + ) +} + +@Composable +fun FilledTextField( + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + readOnly: Boolean = false, + textStyle: TextStyle = LocalTextStyle.current, + label: @Composable (() -> Unit)? = null, + placeholder: @Composable (() -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + supportingText: @Composable (() -> Unit)? = null, + isError: Boolean = false, + visualTransformation: VisualTransformation = VisualTransformation.None, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false, + maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + shape: Shape = TextFieldDefaults.shape, + colors: TextFieldColors = TextFieldDefaults.colors() +) { + androidx.compose.material3.TextField( + value = value, + onValueChange = onValueChange, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + textStyle = textStyle, + label = label, + placeholder = placeholder, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + supportingText = supportingText, + isError = isError, + visualTransformation = visualTransformation, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + maxLines = maxLines, + interactionSource = interactionSource, + shape = shape, + colors = colors, + ) +} + +@Preview(group = PreviewGroup.TextFields) +@Composable +internal fun FilledTextFieldLightPreview() = + ElementPreviewLight { ContentToPreview() } + +@Preview(group = PreviewGroup.TextFields) +@Composable +internal fun FilledTextFieldDarkPreview() = + ElementPreviewDark { ContentToPreview() } + +@ExcludeFromCoverage +@Composable +private fun ContentToPreview() { + Column(modifier = Modifier.padding(4.dp)) { + allBooleans.forEach { isError -> + allBooleans.forEach { enabled -> + allBooleans.forEach { readonly -> + FilledTextField( + value = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}", + onValueChange = {}, + label = { Text(text = "label") }, + isError = isError, + enabled = enabled, + readOnly = readonly, + ) + Spacer(modifier = Modifier.height(2.dp)) + } + } + } + } +} + +@Preview(group = PreviewGroup.TextFields) +@Composable +internal fun FilledTextFieldValueLightPreview() = + ElementPreviewLight { FilledTextFieldValueContentToPreview() } + +@Preview(group = PreviewGroup.TextFields) +@Composable +internal fun FilledTextFieldValueTextFieldDarkPreview() = + ElementPreviewDark { FilledTextFieldValueContentToPreview() } + +@ExcludeFromCoverage +@Composable +private fun FilledTextFieldValueContentToPreview() { + Column(modifier = Modifier.padding(4.dp)) { + allBooleans.forEach { isError -> + allBooleans.forEach { enabled -> + allBooleans.forEach { readonly -> + FilledTextField( + value = TextFieldValue( + text = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}", + selection = TextRange(0, "Hello".length), + ), + onValueChange = {}, + label = { Text(text = "label") }, + isError = isError, + enabled = enabled, + readOnly = readonly, + ) + Spacer(modifier = Modifier.height(2.dp)) + } + } + } + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt index e0d06f1332..b36e83b6ba 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt @@ -7,36 +7,37 @@ package io.element.android.libraries.designsystem.theme.components +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsFocusedAsState +import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.TextFieldColors -import androidx.compose.material3.TextFieldDefaults +import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.autofill.AutofillNode -import androidx.compose.ui.autofill.AutofillType -import androidx.compose.ui.composed -import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.layout.boundsInWindow -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalAutofill -import androidx.compose.ui.platform.LocalAutofillTree -import androidx.compose.ui.text.TextRange +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight @@ -44,161 +45,265 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.utils.allBooleans import io.element.android.libraries.designsystem.utils.asInt +/** + * https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=2008-37137 + */ @Composable fun TextField( value: String, onValueChange: (String) -> Unit, - modifier: Modifier = Modifier, - enabled: Boolean = true, - readOnly: Boolean = false, - textStyle: TextStyle = LocalTextStyle.current, - label: @Composable (() -> Unit)? = null, - placeholder: @Composable (() -> Unit)? = null, + label: String? = null, + supportingText: String? = null, + placeholder: String? = null, leadingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null, - supportingText: @Composable (() -> Unit)? = null, isError: Boolean = false, - visualTransformation: VisualTransformation = VisualTransformation.None, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - keyboardActions: KeyboardActions = KeyboardActions.Default, + enabled: Boolean = true, + readOnly: Boolean = false, singleLine: Boolean = false, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + minLines: Int = 1, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + visualTransformation: VisualTransformation = VisualTransformation.None, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - shape: Shape = TextFieldDefaults.shape, - colors: TextFieldColors = TextFieldDefaults.colors( - unfocusedContainerColor = ElementTheme.colors.bgSubtleSecondary, - focusedContainerColor = ElementTheme.colors.bgSubtleSecondary, - disabledContainerColor = ElementTheme.colors.bgSubtleSecondary, - errorContainerColor = ElementTheme.colors.bgSubtleSecondary, - ) + onTextLayout: (TextLayoutResult) -> Unit = {}, + modifier: Modifier = Modifier ) { - androidx.compose.material3.TextField( + val isFocused by interactionSource.collectIsFocusedAsState() + BasicTextField( value = value, onValueChange = onValueChange, modifier = modifier, + textStyle = textFieldStyle(enabled), + interactionSource = interactionSource, enabled = enabled, - readOnly = readOnly, - textStyle = textStyle, - label = label, - placeholder = placeholder, - leadingIcon = leadingIcon, - trailingIcon = trailingIcon, - supportingText = supportingText, - isError = isError, - visualTransformation = visualTransformation, - keyboardOptions = keyboardOptions, - keyboardActions = keyboardActions, singleLine = singleLine, maxLines = maxLines, - interactionSource = interactionSource, - shape = shape, - colors = colors, - ) + minLines = minLines, + readOnly = readOnly, + cursorBrush = SolidColor(ElementTheme.colors.textPrimary), + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + ) { innerTextField -> + DecorationBox( + label = label, + readOnly = readOnly, + enabled = enabled, + isFocused = isFocused, + isError = isError, + leadingIcon = leadingIcon, + placeholder = placeholder, + isTextEmpty = value.isEmpty(), + innerTextField = innerTextField, + trailingIcon = trailingIcon, + supportingText = supportingText + ) + } } @Composable fun TextField( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, - modifier: Modifier = Modifier, - enabled: Boolean = true, - readOnly: Boolean = false, - textStyle: TextStyle = LocalTextStyle.current, - label: @Composable (() -> Unit)? = null, - placeholder: @Composable (() -> Unit)? = null, + label: String? = null, + supportingText: String? = null, + placeholder: String? = null, leadingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null, - supportingText: @Composable (() -> Unit)? = null, isError: Boolean = false, - visualTransformation: VisualTransformation = VisualTransformation.None, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - keyboardActions: KeyboardActions = KeyboardActions.Default, + enabled: Boolean = true, + readOnly: Boolean = false, singleLine: Boolean = false, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + minLines: Int = 1, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + visualTransformation: VisualTransformation = VisualTransformation.None, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - shape: Shape = TextFieldDefaults.shape, - colors: TextFieldColors = TextFieldDefaults.colors() + onTextLayout: (TextLayoutResult) -> Unit = {}, + modifier: Modifier = Modifier ) { - androidx.compose.material3.TextField( + val isFocused by interactionSource.collectIsFocusedAsState() + BasicTextField( value = value, onValueChange = onValueChange, modifier = modifier, + textStyle = textFieldStyle(enabled), + interactionSource = interactionSource, enabled = enabled, - readOnly = readOnly, - textStyle = textStyle, - label = label, - placeholder = placeholder, - leadingIcon = leadingIcon, - trailingIcon = trailingIcon, - supportingText = supportingText, - isError = isError, - visualTransformation = visualTransformation, - keyboardOptions = keyboardOptions, - keyboardActions = keyboardActions, singleLine = singleLine, maxLines = maxLines, - interactionSource = interactionSource, - shape = shape, - colors = colors, + minLines = minLines, + readOnly = readOnly, + cursorBrush = SolidColor(ElementTheme.colors.textPrimary), + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + ) { innerTextField -> + DecorationBox( + label = label, + readOnly = readOnly, + enabled = enabled, + isFocused = isFocused, + isError = isError, + leadingIcon = leadingIcon, + placeholder = placeholder, + isTextEmpty = value.text.isEmpty(), + innerTextField = innerTextField, + trailingIcon = trailingIcon, + supportingText = supportingText + ) + } +} + + +@Composable +private fun DecorationBox( + label: String?, + enabled: Boolean, + readOnly: Boolean, + isFocused: Boolean, + isError: Boolean, + placeholder: String?, + isTextEmpty: Boolean, + supportingText: String?, + leadingIcon: @Composable (() -> Unit)?, + trailingIcon: @Composable (() -> Unit)?, + innerTextField: @Composable () -> Unit, +) { + Column { + if (label != null) { + Text( + text = label, + color = ElementTheme.colors.textPrimary, + style = ElementTheme.typography.fontBodyMdRegular, + ) + Spacer(modifier = Modifier.height(8.dp)) + } + TextFieldContainer( + enabled = enabled, + readOnly = readOnly, + isFocused = isFocused, + isError = isError + ) { + Row(modifier = Modifier.padding(16.dp)) { + if (leadingIcon != null) { + CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconSecondary) { + leadingIcon() + } + Spacer(modifier = Modifier.width(8.dp)) + } + Box(modifier = Modifier.weight(1f)) { + if (placeholder != null && isTextEmpty) { + Text( + text = placeholder, + color = ElementTheme.colors.textPlaceholder, + style = ElementTheme.typography.fontBodyLgRegular, + ) + } + innerTextField() + } + if (trailingIcon != null) { + Spacer(modifier = Modifier.width(8.dp)) + CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconSecondary) { + trailingIcon() + } + } + } + } + if (supportingText != null) { + Spacer(modifier = Modifier.height(4.dp)) + SupportingTextLayout(isError, supportingText) + } + } +} + +@Composable +private fun TextFieldContainer( + enabled: Boolean, + readOnly: Boolean, + isFocused: Boolean, + isError: Boolean, + content: @Composable () -> Unit +) { + Surface( + shape = RoundedCornerShape(4.dp), + border = if (readOnly) { + null + } else { + BorderStroke( + width = if (isFocused) 2.dp else 1.dp, + color = when { + !enabled -> ElementTheme.colors.borderDisabled + isError -> ElementTheme.colors.borderCriticalPrimary + isFocused -> ElementTheme.colors.borderInteractiveHovered + else -> ElementTheme.colors.borderInteractiveSecondary + } + ) + }, + color = when { + readOnly -> ElementTheme.colors.bgSubtleSecondary + !enabled -> ElementTheme.colors.bgCanvasDisabled + else -> ElementTheme.colors.bgCanvasDefault + }, + content = content + ) +} + +@Composable +private fun SupportingTextLayout(isError: Boolean, supportingText: String) { + Row(horizontalArrangement = spacedBy(4.dp)) { + if (isError) { + Icon( + imageVector = CompoundIcons.Error(), + contentDescription = null, + modifier = Modifier.size(16.dp), + tint = ElementTheme.colors.iconCriticalPrimary + ) + } + Text( + text = supportingText, + color = if (isError) ElementTheme.colors.textCriticalPrimary else ElementTheme.colors.textSecondary, + style = ElementTheme.typography.fontBodySmRegular, + ) + } +} + +@Composable +private fun textFieldStyle(enabled: Boolean): TextStyle { + return ElementTheme.typography.fontBodyLgRegular.copy( + color = if (enabled) { + ElementTheme.colors.textPrimary + } else { + ElementTheme.colors.textSecondary + } ) } @Preview(group = PreviewGroup.TextFields) @Composable -internal fun TextFieldLightPreview() = - ElementPreviewLight { ContentToPreview() } +internal fun TextFieldsLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview(group = PreviewGroup.TextFields) @Composable -internal fun TextFieldDarkPreview() = - ElementPreviewDark { ContentToPreview() } +internal fun TextFieldsDarkPreview() = ElementPreviewDark { ContentToPreview() } -@ExcludeFromCoverage @Composable +@ExcludeFromCoverage private fun ContentToPreview() { Column(modifier = Modifier.padding(4.dp)) { allBooleans.forEach { isError -> allBooleans.forEach { enabled -> allBooleans.forEach { readonly -> TextField( + onValueChange = {}, + label = "Label", value = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}", - onValueChange = {}, - label = { Text(text = "label") }, - isError = isError, - enabled = enabled, - readOnly = readonly, - ) - Spacer(modifier = Modifier.height(2.dp)) - } - } - } - } -} - -@Preview(group = PreviewGroup.TextFields) -@Composable -internal fun TextFieldValueLightPreview() = - ElementPreviewLight { TextFieldValueContentToPreview() } - -@Preview(group = PreviewGroup.TextFields) -@Composable -internal fun TextFieldValueTextFieldDarkPreview() = - ElementPreviewDark { TextFieldValueContentToPreview() } - -@ExcludeFromCoverage -@Composable -private fun TextFieldValueContentToPreview() { - Column(modifier = Modifier.padding(4.dp)) { - allBooleans.forEach { isError -> - allBooleans.forEach { enabled -> - allBooleans.forEach { readonly -> - TextField( - value = TextFieldValue( - text = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}", - selection = TextRange(0, "Hello".length), - ), - onValueChange = {}, - label = { Text(text = "label") }, + supportingText = "Supporting text", isError = isError, enabled = enabled, readOnly = readonly, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField2.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField2.kt deleted file mode 100644 index a7f0d9224e..0000000000 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField2.kt +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 2023, 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only - * Please see LICENSE in the repository root for full details. - */ - -package io.element.android.libraries.designsystem.theme.components - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.collectIsFocusedAsState -import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.BasicTextField -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.LocalContentColor -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.text.TextLayoutResult -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme -import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage -import io.element.android.libraries.designsystem.preview.ElementPreviewDark -import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.allBooleans -import io.element.android.libraries.designsystem.utils.asInt - -/** - * https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=2008-37137 - */ -@Composable -fun TextField2( - value: String, - onValueChange: (String) -> Unit, - label: String? = null, - supportingText: String? = null, - placeholder: String? = null, - leadingIcon: @Composable (() -> Unit)? = null, - trailingIcon: @Composable (() -> Unit)? = null, - isError: Boolean = false, - enabled: Boolean = true, - readOnly: Boolean = false, - singleLine: Boolean = false, - maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, - minLines: Int = 1, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - keyboardActions: KeyboardActions = KeyboardActions.Default, - visualTransformation: VisualTransformation = VisualTransformation.None, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - onTextLayout: (TextLayoutResult) -> Unit = {}, - modifier: Modifier = Modifier -) { - val isFocused by interactionSource.collectIsFocusedAsState() - BasicTextField( - value = value, - onValueChange = onValueChange, - modifier = modifier, - textStyle = textFieldStyle(enabled), - interactionSource = interactionSource, - enabled = enabled, - singleLine = singleLine, - maxLines = maxLines, - minLines = minLines, - readOnly = readOnly, - cursorBrush = SolidColor(ElementTheme.colors.textPrimary), - keyboardOptions = keyboardOptions, - keyboardActions = keyboardActions, - visualTransformation = visualTransformation, - onTextLayout = onTextLayout, - ) { innerTextField -> - DecorationBox( - label = label, - readOnly = readOnly, - enabled = enabled, - isFocused = isFocused, - isError = isError, - leadingIcon = leadingIcon, - placeholder = placeholder, - isTextEmpty = value.isEmpty(), - innerTextField = innerTextField, - trailingIcon = trailingIcon, - supportingText = supportingText - ) - } -} - -@Composable -fun TextField2( - value: TextFieldValue, - onValueChange: (TextFieldValue) -> Unit, - label: String? = null, - supportingText: String? = null, - placeholder: String? = null, - leadingIcon: @Composable (() -> Unit)? = null, - trailingIcon: @Composable (() -> Unit)? = null, - isError: Boolean = false, - enabled: Boolean = true, - readOnly: Boolean = false, - singleLine: Boolean = false, - maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, - minLines: Int = 1, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - keyboardActions: KeyboardActions = KeyboardActions.Default, - visualTransformation: VisualTransformation = VisualTransformation.None, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - onTextLayout: (TextLayoutResult) -> Unit = {}, - modifier: Modifier = Modifier -) { - val isFocused by interactionSource.collectIsFocusedAsState() - BasicTextField( - value = value, - onValueChange = onValueChange, - modifier = modifier, - textStyle = textFieldStyle(enabled), - interactionSource = interactionSource, - enabled = enabled, - singleLine = singleLine, - maxLines = maxLines, - minLines = minLines, - readOnly = readOnly, - cursorBrush = SolidColor(ElementTheme.colors.textPrimary), - keyboardOptions = keyboardOptions, - keyboardActions = keyboardActions, - visualTransformation = visualTransformation, - onTextLayout = onTextLayout, - ) { innerTextField -> - DecorationBox( - label = label, - readOnly = readOnly, - enabled = enabled, - isFocused = isFocused, - isError = isError, - leadingIcon = leadingIcon, - placeholder = placeholder, - isTextEmpty = value.text.isEmpty(), - innerTextField = innerTextField, - trailingIcon = trailingIcon, - supportingText = supportingText - ) - } -} - - -@Composable -private fun DecorationBox( - label: String?, - enabled: Boolean, - readOnly: Boolean, - isFocused: Boolean, - isError: Boolean, - placeholder: String?, - isTextEmpty: Boolean, - supportingText: String?, - leadingIcon: @Composable (() -> Unit)?, - trailingIcon: @Composable (() -> Unit)?, - innerTextField: @Composable () -> Unit, -) { - Column { - if (label != null) { - Text( - text = label, - color = ElementTheme.colors.textPrimary, - style = ElementTheme.typography.fontBodyMdRegular, - ) - Spacer(modifier = Modifier.height(8.dp)) - } - TextFieldContainer( - enabled = enabled, - readOnly = readOnly, - isFocused = isFocused, - isError = isError - ) { - Row(modifier = Modifier.padding(16.dp)) { - if (leadingIcon != null) { - CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconSecondary) { - leadingIcon() - } - Spacer(modifier = Modifier.width(8.dp)) - } - Box(modifier = Modifier.weight(1f)) { - if (placeholder != null && isTextEmpty) { - Text( - text = placeholder, - color = ElementTheme.colors.textPlaceholder, - style = ElementTheme.typography.fontBodyLgRegular, - ) - } - innerTextField() - } - if (trailingIcon != null) { - Spacer(modifier = Modifier.width(8.dp)) - CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconSecondary) { - trailingIcon() - } - } - } - } - if (supportingText != null) { - Spacer(modifier = Modifier.height(4.dp)) - SupportingTextLayout(isError, supportingText) - } - } -} - -@Composable -private fun TextFieldContainer( - enabled: Boolean, - readOnly: Boolean, - isFocused: Boolean, - isError: Boolean, - content: @Composable () -> Unit -) { - Surface( - shape = RoundedCornerShape(4.dp), - border = if (readOnly) { - null - } else { - BorderStroke( - width = if (isFocused) 2.dp else 1.dp, - color = when { - !enabled -> ElementTheme.colors.borderDisabled - isError -> ElementTheme.colors.borderCriticalPrimary - isFocused -> ElementTheme.colors.borderInteractiveHovered - else -> ElementTheme.colors.borderInteractiveSecondary - } - ) - }, - color = when { - readOnly -> ElementTheme.colors.bgSubtleSecondary - !enabled -> ElementTheme.colors.bgCanvasDisabled - else -> ElementTheme.colors.bgCanvasDefault - }, - content = content - ) -} - -@Composable -private fun SupportingTextLayout(isError: Boolean, supportingText: String) { - Row(horizontalArrangement = spacedBy(4.dp)) { - if (isError) { - Icon( - imageVector = CompoundIcons.Error(), - contentDescription = null, - modifier = Modifier.size(16.dp), - tint = ElementTheme.colors.iconCriticalPrimary - ) - } - Text( - text = supportingText, - color = if (isError) ElementTheme.colors.textCriticalPrimary else ElementTheme.colors.textSecondary, - style = ElementTheme.typography.fontBodySmRegular, - ) - } -} - -@Composable -private fun textFieldStyle(enabled: Boolean): TextStyle { - return ElementTheme.typography.fontBodyLgRegular.copy( - color = if (enabled) { - ElementTheme.colors.textPrimary - } else { - ElementTheme.colors.textSecondary - } - ) -} - -@Preview(group = PreviewGroup.TextFields) -@Composable -internal fun TextFields2LightPreview() = ElementPreviewLight { ContentToPreview() } - -@Preview(group = PreviewGroup.TextFields) -@Composable -internal fun TextFields2DarkPreview() = ElementPreviewDark { ContentToPreview() } - -@Composable -@ExcludeFromCoverage -private fun ContentToPreview() { - Column(modifier = Modifier.padding(4.dp)) { - allBooleans.forEach { isError -> - allBooleans.forEach { enabled -> - allBooleans.forEach { readonly -> - TextField2( - onValueChange = {}, - label = "Label", - value = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}", - supportingText = "Supporting text", - isError = isError, - enabled = enabled, - readOnly = readonly, - ) - Spacer(modifier = Modifier.height(2.dp)) - } - } - } - } -}