Merge pull request #3834 from element-hq/feature/fga/design_system_text_field

Design system : implement new TextField
This commit is contained in:
ganfra 2024-11-12 11:18:07 +01:00 committed by GitHub
commit 79b758c228
188 changed files with 817 additions and 901 deletions

View file

@ -23,7 +23,6 @@ import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -36,16 +35,17 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.features.createroom.impl.R import io.element.android.features.createroom.impl.R
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize
import io.element.android.libraries.designsystem.components.LabelledTextField
import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.ListItem 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.Scaffold
@ -217,7 +217,7 @@ private fun RoomNameWithAvatar(
modifier = Modifier.clickable(onClick = onAvatarClick), modifier = Modifier.clickable(onClick = onAvatarClick),
) )
LabelledTextField( TextField(
label = stringResource(R.string.screen_create_room_room_name_label), label = stringResource(R.string.screen_create_room_room_name_label),
value = roomName, value = roomName,
placeholder = stringResource(CommonStrings.common_room_name_placeholder), placeholder = stringResource(CommonStrings.common_room_name_placeholder),
@ -233,7 +233,7 @@ private fun RoomTopic(
onTopicChange: (String) -> Unit, onTopicChange: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
LabelledTextField( TextField(
modifier = modifier, modifier = modifier,
label = stringResource(R.string.screen_create_room_topic_label), label = stringResource(R.string.screen_create_room_topic_label),
value = topic, value = topic,
@ -322,51 +322,43 @@ private fun RoomAddressField(
onAddressChange: (String) -> Unit, onAddressChange: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Column( TextField(
modifier = modifier, modifier = modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp), value = address.value,
) { label = stringResource(R.string.screen_create_room_room_address_section_title),
Text( leadingIcon = {
modifier = Modifier.padding(horizontal = 16.dp), Text(
style = ElementTheme.typography.fontBodyMdRegular, text = "#",
color = MaterialTheme.colorScheme.primary, style = ElementTheme.typography.fontBodyLgMedium,
text = stringResource(R.string.screen_create_room_room_address_section_title), color = ElementTheme.colors.textSecondary,
) )
},
TextField( trailingIcon = {
modifier = Modifier.fillMaxWidth(), Text(
value = address.value, text = homeserverName,
leadingIcon = { style = ElementTheme.typography.fontBodyLgMedium,
Text( color = ElementTheme.colors.textSecondary,
text = "#", )
style = ElementTheme.typography.fontBodyLgMedium, },
color = ElementTheme.colors.textSecondary, supportingText = stringResource(R.string.screen_create_room_room_address_section_footer),
) onValueChange = onAddressChange,
}, singleLine = true,
trailingIcon = { )
Text(
text = homeserverName,
style = ElementTheme.typography.fontBodyLgMedium,
color = ElementTheme.colors.textSecondary,
modifier = Modifier.padding(end = 16.dp)
)
},
supportingText = {
Text(
text = stringResource(R.string.screen_create_room_room_address_section_footer),
style = ElementTheme.typography.fontBodySmRegular,
color = ElementTheme.colors.textSecondary,
)
},
onValueChange = onAddressChange,
singleLine = true,
)
}
} }
@PreviewsDayNight @PreviewWithLargeHeight
@Composable @Composable
internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreview { internal fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) =
ElementPreviewLight { ContentToPreview(state) }
@PreviewWithLargeHeight
@Composable
internal fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) =
ElementPreviewDark { ContentToPreview(state) }
@ExcludeFromCoverage
@Composable
private fun ContentToPreview(state: ConfigureRoomState) {
ConfigureRoomView( ConfigureRoomView(
state = state, state = state,
onBackClick = {}, onBackClick = {},

View file

@ -9,7 +9,9 @@
package io.element.android.features.logout.impl package io.element.android.features.logout.impl
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@ -52,19 +54,18 @@ import io.element.android.libraries.designsystem.atomic.organisms.InfoListOrgani
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.form.textFieldState import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.components.list.SwitchListItem import io.element.android.libraries.designsystem.components.list.SwitchListItem
import io.element.android.libraries.designsystem.modifiers.autofill
import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart
import io.element.android.libraries.designsystem.theme.aliasScreenTitle 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.Button
import io.element.android.libraries.designsystem.theme.components.Icon 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.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Scaffold 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.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.theme.components.autofill
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -257,26 +258,21 @@ private fun Content(
) )
} }
Column( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 16.dp), .padding(top = 16.dp),
) { ) {
Text(
text = stringResource(CommonStrings.action_confirm_password),
style = ElementTheme.typography.fontBodySmMedium,
color = ElementTheme.colors.textSecondary,
)
var passwordVisible by remember { mutableStateOf(false) } var passwordVisible by remember { mutableStateOf(false) }
if (isLoading) { if (isLoading) {
// Ensure password is hidden when user submits the form // Ensure password is hidden when user submits the form
passwordVisible = false passwordVisible = false
} }
OutlinedTextField( TextField(
value = passwordFieldState, value = passwordFieldState,
label = stringResource(CommonStrings.action_confirm_password),
readOnly = isLoading, readOnly = isLoading,
modifier = Modifier modifier = Modifier
.padding(top = 8.dp)
.fillMaxWidth() .fillMaxWidth()
.onTabOrEnterKeyFocusNext(focusManager) .onTabOrEnterKeyFocusNext(focusManager)
.testTag(TestTags.loginPassword) .testTag(TestTags.loginPassword)
@ -293,9 +289,7 @@ private fun Content(
passwordFieldState = sanitized passwordFieldState = sanitized
eventSink(AccountDeactivationEvents.SetPassword(sanitized)) eventSink(AccountDeactivationEvents.SetPassword(sanitized))
}, },
placeholder = { placeholder = stringResource(CommonStrings.common_password),
Text(text = stringResource(CommonStrings.common_password))
},
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = { trailingIcon = {
val image = val image =
@ -303,7 +297,7 @@ private fun Content(
val description = val description =
if (passwordVisible) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password) if (passwordVisible) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password)
IconButton(onClick = { passwordVisible = !passwordVisible }) { Box(modifier = Modifier.clickable { passwordVisible = !passwordVisible }) {
Icon(imageVector = image, description) Icon(imageVector = image, description)
} }
}, },

View file

@ -60,8 +60,8 @@ 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.Button
import io.element.android.libraries.designsystem.theme.components.ButtonSize 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.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text 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.TopAppBar 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.core.RoomIdOrAlias
import io.element.android.libraries.matrix.api.room.RoomType import io.element.android.libraries.matrix.api.room.RoomType
@ -390,19 +390,13 @@ private fun DefaultLoadedContent(
) )
} else if (contentState.joinAuthorisationStatus is JoinAuthorisationStatus.CanKnock) { } else if (contentState.joinAuthorisationStatus is JoinAuthorisationStatus.CanKnock) {
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
OutlinedTextField( TextField(
value = knockMessage, value = knockMessage,
onValueChange = onKnockMessageUpdate, onValueChange = onKnockMessageUpdate,
maxLines = 3, maxLines = 3,
minLines = 3, minLines = 3,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth(),
) supportingText = stringResource(R.string.screen_join_room_knock_message_description)
Text(
text = stringResource(R.string.screen_join_room_knock_message_description),
style = ElementTheme.typography.fontBodySmRegular,
color = ElementTheme.colors.textPlaceholder,
textAlign = TextAlign.Start,
modifier = Modifier.fillMaxWidth()
) )
} }
} }

View file

@ -7,6 +7,7 @@
package io.element.android.features.login.impl.screens.loginpassword package io.element.android.features.login.impl.screens.loginpassword
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@ -52,17 +53,15 @@ import io.element.android.libraries.designsystem.components.BigIcon
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.form.textFieldState import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.modifiers.autofill
import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight 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.Button
import io.element.android.libraries.designsystem.theme.components.Icon 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.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Scaffold 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.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.theme.components.autofill
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -101,12 +100,12 @@ fun LoginPasswordView(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.imePadding() .imePadding()
.padding(padding) .padding(padding)
.consumeWindowInsets(padding) .consumeWindowInsets(padding)
.verticalScroll(state = scrollState) .verticalScroll(state = scrollState)
.padding(start = 20.dp, end = 20.dp, bottom = 20.dp), .padding(start = 20.dp, end = 20.dp, bottom = 20.dp),
) { ) {
// Title // Title
IconTitleSubtitleMolecule( IconTitleSubtitleMolecule(
@ -140,8 +139,8 @@ fun LoginPasswordView(
onClick = ::submit, onClick = ::submit,
enabled = state.submitEnabled || isLoading, enabled = state.submitEnabled || isLoading,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.testTag(TestTags.loginContinue) .testTag(TestTags.loginContinue)
) )
Spacer(modifier = Modifier.height(48.dp)) Spacer(modifier = Modifier.height(48.dp))
} }
@ -170,16 +169,10 @@ private fun LoginForm(
val eventSink = state.eventSink val eventSink = state.eventSink
Column { Column {
Text( TextField(
text = stringResource(R.string.screen_login_form_header), label = stringResource(R.string.screen_login_form_header),
modifier = Modifier.padding(start = 16.dp),
style = ElementTheme.typography.fontBodyMdRegular,
)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = loginFieldState, value = loginFieldState,
readOnly = isLoading, enabled = !isLoading,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.onTabOrEnterKeyFocusNext(focusManager) .onTabOrEnterKeyFocusNext(focusManager)
@ -192,9 +185,7 @@ private fun LoginForm(
eventSink(LoginPasswordEvents.SetLogin(sanitized)) eventSink(LoginPasswordEvents.SetLogin(sanitized))
} }
), ),
placeholder = { placeholder = stringResource(CommonStrings.common_username),
Text(text = stringResource(CommonStrings.common_username))
},
onValueChange = { onValueChange = {
val sanitized = it.sanitize() val sanitized = it.sanitize()
loginFieldState = sanitized loginFieldState = sanitized
@ -210,10 +201,14 @@ private fun LoginForm(
singleLine = true, singleLine = true,
trailingIcon = if (loginFieldState.isNotEmpty()) { trailingIcon = if (loginFieldState.isNotEmpty()) {
{ {
IconButton(onClick = { Box(Modifier.clickable {
loginFieldState = "" loginFieldState = ""
}) { }) {
Icon(imageVector = CompoundIcons.Close(), contentDescription = stringResource(CommonStrings.action_clear)) Icon(
imageVector = CompoundIcons.Close(),
contentDescription = stringResource(CommonStrings.action_clear),
tint = ElementTheme.colors.iconSecondary
)
} }
} }
} else { } else {
@ -226,9 +221,9 @@ private fun LoginForm(
passwordVisible = false passwordVisible = false
} }
Spacer(Modifier.height(20.dp)) Spacer(Modifier.height(20.dp))
OutlinedTextField( TextField(
value = passwordFieldState, value = passwordFieldState,
readOnly = isLoading, enabled = !isLoading,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.onTabOrEnterKeyFocusNext(focusManager) .onTabOrEnterKeyFocusNext(focusManager)
@ -246,18 +241,18 @@ private fun LoginForm(
passwordFieldState = sanitized passwordFieldState = sanitized
eventSink(LoginPasswordEvents.SetPassword(sanitized)) eventSink(LoginPasswordEvents.SetPassword(sanitized))
}, },
placeholder = { placeholder = stringResource(CommonStrings.common_password),
Text(text = stringResource(CommonStrings.common_password))
},
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = { trailingIcon = {
val image = val image =
if (passwordVisible) CompoundIcons.VisibilityOn() else CompoundIcons.VisibilityOff() if (passwordVisible) CompoundIcons.VisibilityOn() else CompoundIcons.VisibilityOff()
val description = val description =
if (passwordVisible) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password) if (passwordVisible) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password)
Box(Modifier.clickable { passwordVisible = !passwordVisible }) {
IconButton(onClick = { passwordVisible = !passwordVisible }) { Icon(
Icon(imageVector = image, description) imageVector = image,
contentDescription = description,
)
} }
}, },
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(

View file

@ -9,6 +9,7 @@
package io.element.android.features.login.impl.screens.searchaccountprovider package io.element.android.features.login.impl.screens.searchaccountprovider
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.consumeWindowInsets
@ -23,7 +24,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -51,14 +51,12 @@ import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubti
import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.BigIcon
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.form.textFieldState import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight 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.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Icon 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.TextField
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -86,10 +84,10 @@ fun SearchAccountProviderView(
) { padding -> ) { padding ->
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.imePadding() .imePadding()
.padding(padding) .padding(padding)
.consumeWindowInsets(padding) .consumeWindowInsets(padding)
) { ) {
LazyColumn(modifier = Modifier.fillMaxWidth(), state = rememberLazyListState()) { LazyColumn(modifier = Modifier.fillMaxWidth(), state = rememberLazyListState()) {
item { item {
@ -104,7 +102,7 @@ fun SearchAccountProviderView(
// TextInput // TextInput
var userInputState by textFieldState(stateValue = state.userInput) var userInputState by textFieldState(stateValue = state.userInput)
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
OutlinedTextField( TextField(
value = userInputState, value = userInputState,
// readOnly = isLoading, // readOnly = isLoading,
modifier = Modifier modifier = Modifier
@ -126,7 +124,7 @@ fun SearchAccountProviderView(
singleLine = true, singleLine = true,
trailingIcon = if (userInputState.isNotEmpty()) { trailingIcon = if (userInputState.isNotEmpty()) {
{ {
IconButton(onClick = { Box(Modifier.clickable {
userInputState = "" userInputState = ""
eventSink(SearchAccountProviderEvents.UserInput("")) eventSink(SearchAccountProviderEvents.UserInput(""))
}) { }) {
@ -139,9 +137,7 @@ fun SearchAccountProviderView(
} else { } else {
null null
}, },
supportingText = { supportingText = stringResource(id = R.string.screen_account_provider_form_notice),
Text(text = stringResource(id = R.string.screen_account_provider_form_notice), color = MaterialTheme.colorScheme.secondary)
}
) )
} }

View file

@ -27,7 +27,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
@ -39,9 +38,9 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.aliasScreenTitle 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.Button
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Scaffold 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.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -89,21 +88,16 @@ fun ReportMessageView(
) { ) {
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
OutlinedTextField( TextField(
value = state.reason, value = state.reason,
onValueChange = { state.eventSink(ReportMessageEvents.UpdateReason(it)) }, onValueChange = { state.eventSink(ReportMessageEvents.UpdateReason(it)) },
placeholder = { Text(stringResource(R.string.screen_report_content_hint)) }, placeholder = stringResource(R.string.screen_report_content_hint),
minLines = 3,
enabled = !isSending, enabled = !isSending,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(min = 90.dp) .heightIn(min = 90.dp),
) supportingText = stringResource(R.string.screen_report_content_explanation),
Text(
text = stringResource(R.string.screen_report_content_explanation),
style = ElementTheme.typography.fontBodySmRegular,
color = MaterialTheme.colorScheme.secondary,
textAlign = TextAlign.Start,
modifier = Modifier.padding(top = 4.dp, bottom = 24.dp, start = 16.dp, end = 16.dp)
) )
Row( Row(

View file

@ -33,7 +33,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.poll.impl.R import io.element.android.features.poll.impl.R
@ -48,10 +47,10 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.ListItemStyle
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Scaffold 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.Text
import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -109,14 +108,10 @@ fun CreatePollView(
) { ) {
item { item {
Column { Column {
Text(
text = stringResource(id = R.string.screen_create_poll_question_desc),
modifier = Modifier.padding(start = 32.dp),
style = ElementTheme.typography.fontBodyMdRegular,
)
ListItem( ListItem(
headlineContent = { headlineContent = {
OutlinedTextField( TextField(
label = stringResource(id = R.string.screen_create_poll_question_desc),
value = state.question, value = state.question,
onValueChange = { onValueChange = {
state.eventSink(CreatePollEvents.SetQuestion(it)) state.eventSink(CreatePollEvents.SetQuestion(it))
@ -124,9 +119,7 @@ fun CreatePollView(
modifier = Modifier modifier = Modifier
.focusRequester(questionFocusRequester) .focusRequester(questionFocusRequester)
.fillMaxWidth(), .fillMaxWidth(),
placeholder = { placeholder = stringResource(id = R.string.screen_create_poll_question_hint),
Text(text = stringResource(id = R.string.screen_create_poll_question_hint))
},
keyboardOptions = keyboardOptions, keyboardOptions = keyboardOptions,
) )
} }
@ -137,7 +130,7 @@ fun CreatePollView(
val isLastItem = index == state.answers.size - 1 val isLastItem = index == state.answers.size - 1
ListItem( ListItem(
headlineContent = { headlineContent = {
OutlinedTextField( TextField(
value = answer.text, value = answer.text,
onValueChange = { onValueChange = {
state.eventSink(CreatePollEvents.SetAnswer(index, it)) state.eventSink(CreatePollEvents.SetAnswer(index, it))
@ -145,9 +138,7 @@ fun CreatePollView(
modifier = Modifier modifier = Modifier
.then(if (isLastItem) Modifier.focusRequester(answerFocusRequester) else Modifier) .then(if (isLastItem) Modifier.focusRequester(answerFocusRequester) else Modifier)
.fillMaxWidth(), .fillMaxWidth(),
placeholder = { placeholder = stringResource(id = R.string.screen_create_poll_answer_hint, index + 1),
Text(text = stringResource(id = R.string.screen_create_poll_answer_hint, index + 1))
},
keyboardOptions = keyboardOptions, keyboardOptions = keyboardOptions,
) )
}, },

View file

@ -29,7 +29,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.R
import io.element.android.libraries.designsystem.components.LabelledOutlinedTextField
import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@ -41,6 +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.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text 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.TextButton
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar 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.AvatarActionBottomSheet
import io.element.android.libraries.matrix.ui.components.EditableAvatarView import io.element.android.libraries.matrix.ui.components.EditableAvatarView
@ -112,7 +112,7 @@ fun EditUserProfileView(
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
Spacer(modifier = Modifier.height(40.dp)) Spacer(modifier = Modifier.height(40.dp))
LabelledOutlinedTextField( TextField(
label = stringResource(R.string.screen_edit_profile_display_name), label = stringResource(R.string.screen_edit_profile_display_name),
value = state.displayName, value = state.displayName,
placeholder = stringResource(CommonStrings.common_room_name_placeholder), placeholder = stringResource(CommonStrings.common_room_name_placeholder),

View file

@ -39,13 +39,12 @@ import io.element.android.libraries.designsystem.components.preferences.Preferen
import io.element.android.libraries.designsystem.components.preferences.PreferenceRow import io.element.android.libraries.designsystem.components.preferences.PreferenceRow
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground 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.Button
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@Composable @Composable
@ -70,17 +69,14 @@ fun BugReportView(
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
PreferenceRow { PreferenceRow {
OutlinedTextField( TextField(
value = descriptionFieldState, value = descriptionFieldState,
modifier = Modifier.fillMaxWidth() modifier = Modifier
.onTabOrEnterKeyFocusNext(LocalFocusManager.current), .fillMaxWidth()
.onTabOrEnterKeyFocusNext(LocalFocusManager.current),
enabled = isFormEnabled, enabled = isFormEnabled,
label = { placeholder = stringResource(id = R.string.screen_bug_report_editor_placeholder),
Text(text = stringResource(id = R.string.screen_bug_report_editor_placeholder)) supportingText = stringResource(id = R.string.screen_bug_report_editor_description),
},
supportingText = {
Text(text = stringResource(id = R.string.screen_bug_report_editor_description))
},
onValueChange = { onValueChange = {
descriptionFieldState = it descriptionFieldState = it
eventSink(BugReportEvents.SetDescription(it)) eventSink(BugReportEvents.SetDescription(it))
@ -152,8 +148,8 @@ fun BugReportView(
enabled = state.submitEnabled, enabled = state.submitEnabled,
showProgress = state.sending.isLoading(), showProgress = state.sending.isLoading(),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 24.dp, bottom = 16.dp) .padding(top = 24.dp, bottom = 16.dp)
) )
} }
} }

View file

@ -9,7 +9,6 @@
package io.element.android.features.roomdetails.impl.edit package io.element.android.features.roomdetails.impl.edit
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -21,7 +20,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -33,7 +31,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.features.roomdetails.impl.R import io.element.android.features.roomdetails.impl.R
import io.element.android.libraries.designsystem.components.LabelledTextField
import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@ -45,6 +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.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text 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.TextButton
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar 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.AvatarActionBottomSheet
import io.element.android.libraries.matrix.ui.components.EditableAvatarView import io.element.android.libraries.matrix.ui.components.EditableAvatarView
@ -110,40 +108,28 @@ fun RoomDetailsEditView(
) )
Spacer(modifier = Modifier.height(60.dp)) Spacer(modifier = Modifier.height(60.dp))
if (state.canChangeName) { TextField(
LabelledTextField( label = stringResource(id = R.string.screen_room_details_room_name_label),
label = stringResource(id = R.string.screen_room_details_room_name_label), value = state.roomRawName,
value = state.roomRawName, placeholder = stringResource(CommonStrings.common_room_name_placeholder),
placeholder = stringResource(CommonStrings.common_room_name_placeholder), singleLine = true,
singleLine = true, readOnly = !state.canChangeName,
onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomName(it)) }, onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomName(it)) },
) )
} else {
LabelledReadOnlyField(
title = stringResource(R.string.screen_room_details_room_name_label),
value = state.roomRawName
)
}
Spacer(modifier = Modifier.height(28.dp)) Spacer(modifier = Modifier.height(28.dp))
if (state.canChangeTopic) { TextField(
LabelledTextField( label = stringResource(CommonStrings.common_topic),
label = stringResource(CommonStrings.common_topic), value = state.roomTopic,
value = state.roomTopic, placeholder = stringResource(CommonStrings.common_topic_placeholder),
placeholder = stringResource(CommonStrings.common_topic_placeholder), maxLines = 10,
maxLines = 10, readOnly = !state.canChangeTopic,
onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(it)) }, onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(it)) },
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, capitalization = KeyboardCapitalization.Sentences,
), ),
) )
} else {
LabelledReadOnlyField(
title = stringResource(R.string.screen_room_details_topic_title),
value = state.roomTopic
)
}
} }
} }
@ -171,30 +157,6 @@ fun RoomDetailsEditView(
) )
} }
@Composable
private fun LabelledReadOnlyField(
title: String,
value: String,
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
modifier = Modifier.padding(horizontal = 16.dp),
style = ElementTheme.typography.fontBodyMdRegular,
color = MaterialTheme.colorScheme.primary,
text = title,
)
Text(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 12.dp),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.primary,
text = value,
)
}
}
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun RoomDetailsEditViewPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = ElementPreview { internal fun RoomDetailsEditViewPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = ElementPreview {

View file

@ -9,9 +9,9 @@ package io.element.android.features.roomdetails.edit
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.ui.test.assertHasClickAction import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertHasNoClickAction
import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.isEditable
import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onNodeWithText
@ -78,7 +78,6 @@ class RoomDetailsEditViewTest {
roomRawName = "Marketing", roomRawName = "Marketing",
), ),
) )
rule.onNodeWithText("Marketing").assertHasClickAction()
rule.onNodeWithText("Marketing").performTextInput("A") rule.onNodeWithText("Marketing").performTextInput("A")
eventsRecorder.assertSingle(RoomDetailsEditEvents.UpdateRoomName("AMarketing")) eventsRecorder.assertSingle(RoomDetailsEditEvents.UpdateRoomName("AMarketing"))
} }
@ -93,7 +92,7 @@ class RoomDetailsEditViewTest {
canChangeName = false, canChangeName = false,
), ),
) )
rule.onNodeWithText("Marketing").assertHasNoClickAction() rule.onNodeWithText("Marketing").assert(!isEditable())
} }
@Test @Test
@ -105,7 +104,6 @@ class RoomDetailsEditViewTest {
roomTopic = "My Topic", roomTopic = "My Topic",
), ),
) )
rule.onNodeWithText("My Topic").assertHasClickAction()
rule.onNodeWithText("My Topic").performTextInput("A") rule.onNodeWithText("My Topic").performTextInput("A")
eventsRecorder.assertSingle(RoomDetailsEditEvents.UpdateRoomTopic("AMy Topic")) eventsRecorder.assertSingle(RoomDetailsEditEvents.UpdateRoomTopic("AMy Topic"))
} }
@ -120,7 +118,7 @@ class RoomDetailsEditViewTest {
canChangeTopic = false, canChangeTopic = false,
), ),
) )
rule.onNodeWithText("My Topic").assertHasNoClickAction() rule.onNodeWithText("My Topic").assert(!isEditable())
} }
@Ignore("This test is failing because the bottom sheet does not open") @Ignore("This test is failing because the bottom sheet does not open")

View file

@ -45,11 +45,11 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.FilledTextField
import io.element.android.libraries.designsystem.theme.components.Icon 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.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold 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.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -194,7 +194,7 @@ private fun SearchTextField(
), ),
) { ) {
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
TextField( FilledTextField(
modifier = modifier.testTag(TestTags.searchTextField.value), modifier = modifier.testTag(TestTags.searchTextField.value),
textStyle = ElementTheme.typography.fontBodyLgRegular, textStyle = ElementTheme.typography.fontBodyLgRegular,
singleLine = true, singleLine = true,

View file

@ -49,11 +49,11 @@ import io.element.android.libraries.designsystem.modifiers.applyIf
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.FilledTextField
import io.element.android.libraries.designsystem.theme.components.Icon 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.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold 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.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.copy import io.element.android.libraries.designsystem.utils.copy
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@ -130,7 +130,7 @@ private fun RoomListSearchContent(
title = { title = {
val filter = state.query val filter = state.query
val focusRequester = FocusRequester() val focusRequester = FocusRequester()
TextField( FilledTextField(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.focusRequester(focusRequester), .focusRequester(focusRequester),

View file

@ -7,6 +7,8 @@
package io.element.android.features.securebackup.impl.reset.password package io.element.android.features.securebackup.impl.reset.password
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -25,14 +27,12 @@ import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage
import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.BigIcon
import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.form.textFieldState import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight 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.Button
import io.element.android.libraries.designsystem.theme.components.Icon 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.TextField
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@Composable @Composable
@ -80,14 +80,13 @@ fun ResetIdentityPasswordView(
@Composable @Composable
private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Boolean) { private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Boolean) {
var showPassword by remember { mutableStateOf(false) } var showPassword by remember { mutableStateOf(false) }
OutlinedTextField( TextField(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.onTabOrEnterKeyFocusNext(LocalFocusManager.current), .onTabOrEnterKeyFocusNext(LocalFocusManager.current),
value = text, value = text,
onValueChange = onTextChange, onValueChange = onTextChange,
label = { Text(stringResource(CommonStrings.common_password)) }, placeholder = stringResource(CommonStrings.common_password),
placeholder = { Text(stringResource(R.string.screen_reset_encryption_password_placeholder)) },
singleLine = true, singleLine = true,
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(), visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = { trailingIcon = {
@ -96,13 +95,13 @@ private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Bool
val description = val description =
if (showPassword) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password) if (showPassword) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password)
IconButton(onClick = { showPassword = !showPassword }) { Box(Modifier.clickable { showPassword = !showPassword }) {
Icon(imageVector = image, description) Icon(imageVector = image, description)
} }
}, },
isError = hasError, isError = hasError,
supportingText = if (hasError) { supportingText = if (hasError) {
{ Text(stringResource(R.string.screen_reset_encryption_password_error)) } stringResource(R.string.screen_reset_encryption_password_error)
} else { } else {
null null
} }

View file

@ -36,14 +36,14 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.securebackup.impl.R import io.element.android.features.securebackup.impl.R
import io.element.android.features.securebackup.impl.tools.RecoveryKeyVisualTransformation import io.element.android.features.securebackup.impl.tools.RecoveryKeyVisualTransformation
import io.element.android.libraries.designsystem.modifiers.autofill
import io.element.android.libraries.designsystem.modifiers.clickableIfNotNull import io.element.android.libraries.designsystem.modifiers.clickableIfNotNull
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight 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.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.autofill import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -62,7 +62,6 @@ internal fun RecoveryKeyView(
) { ) {
Text( Text(
text = stringResource(id = CommonStrings.common_recovery_key), text = stringResource(id = CommonStrings.common_recovery_key),
modifier = Modifier.padding(start = 16.dp),
style = ElementTheme.typography.fontBodyMdRegular, style = ElementTheme.typography.fontBodyMdRegular,
) )
RecoveryKeyContent(state, onClick, onChange, onSubmit) RecoveryKeyContent(state, onClick, onChange, onSubmit)
@ -159,7 +158,7 @@ private fun RecoveryKeyFormContent(
// Do not apply a visual transformation if the key has spaces, to let user enter passphrase // Do not apply a visual transformation if the key has spaces, to let user enter passphrase
if (keyHasSpace) VisualTransformation.None else RecoveryKeyVisualTransformation() if (keyHasSpace) VisualTransformation.None else RecoveryKeyVisualTransformation()
} }
OutlinedTextField( TextField(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.testTag(TestTags.recoveryKey) .testTag(TestTags.recoveryKey)
@ -179,7 +178,7 @@ private fun RecoveryKeyFormContent(
keyboardActions = KeyboardActions( keyboardActions = KeyboardActions(
onDone = { onSubmit() } onDone = { onSubmit() }
), ),
label = { Text(text = stringResource(id = R.string.screen_recovery_key_confirm_key_placeholder)) } placeholder = stringResource(id = R.string.screen_recovery_key_confirm_key_placeholder),
) )
} }
@ -198,14 +197,12 @@ private fun RecoveryKeyFooter(state: RecoveryKeyViewState) {
} }
), ),
color = ElementTheme.colors.textSecondary, color = ElementTheme.colors.textSecondary,
modifier = Modifier.padding(start = 16.dp),
style = ElementTheme.typography.fontBodySmRegular, style = ElementTheme.typography.fontBodySmRegular,
) )
} else { } else {
Text( Text(
text = stringResource(id = R.string.screen_recovery_key_save_key_description), text = stringResource(id = R.string.screen_recovery_key_save_key_description),
color = ElementTheme.colors.textSecondary, color = ElementTheme.colors.textSecondary,
modifier = Modifier.padding(start = 16.dp),
style = ElementTheme.typography.fontBodySmRegular, style = ElementTheme.typography.fontBodySmRegular,
) )
} }
@ -214,7 +211,6 @@ private fun RecoveryKeyFooter(state: RecoveryKeyViewState) {
Text( Text(
text = stringResource(id = R.string.screen_recovery_key_confirm_key_description), text = stringResource(id = R.string.screen_recovery_key_confirm_key_description),
color = ElementTheme.colors.textSecondary, color = ElementTheme.colors.textSecondary,
modifier = Modifier.padding(start = 16.dp),
style = ElementTheme.typography.fontBodySmRegular, style = ElementTheme.typography.fontBodySmRegular,
) )
} }

View file

@ -1,76 +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.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun LabelledOutlinedTextField(
label: String,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
placeholder: String? = null,
singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
modifier = Modifier.padding(horizontal = 16.dp),
style = ElementTheme.typography.fontBodyMdRegular,
color = MaterialTheme.colorScheme.primary,
text = label
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = value,
placeholder = placeholder?.let { { Text(placeholder) } },
onValueChange = onValueChange,
singleLine = singleLine,
maxLines = maxLines,
keyboardOptions = keyboardOptions,
)
}
}
@PreviewsDayNight
@Composable
internal fun LabelledOutlinedTextFieldPreview() = ElementPreview {
Column {
LabelledOutlinedTextField(
label = "Room name",
value = "",
onValueChange = {},
placeholder = "e.g. Product Sprint",
)
LabelledOutlinedTextField(
label = "Room name",
value = "a room name",
onValueChange = {},
placeholder = "e.g. Product Sprint",
)
}
}

View file

@ -1,76 +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.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextField
@Composable
fun LabelledTextField(
label: String,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
placeholder: String? = null,
singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
modifier = Modifier.padding(horizontal = 16.dp),
style = ElementTheme.typography.fontBodyMdRegular,
color = MaterialTheme.colorScheme.primary,
text = label
)
TextField(
modifier = Modifier.fillMaxWidth(),
value = value,
placeholder = placeholder?.let { { Text(placeholder) } },
onValueChange = onValueChange,
singleLine = singleLine,
maxLines = maxLines,
keyboardOptions = keyboardOptions,
)
}
}
@PreviewsDayNight
@Composable
internal fun LabelledTextFieldPreview() = ElementPreview {
Column {
LabelledTextField(
label = "Room name",
value = "",
onValueChange = {},
placeholder = "e.g. Product Sprint",
)
LabelledTextField(
label = "Room name",
value = "a room name",
onValueChange = {},
placeholder = "e.g. Product Sprint",
)
}
}

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.designsystem.components.list
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -18,7 +19,6 @@ import androidx.compose.ui.tooling.preview.Preview
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
@Composable @Composable

View file

@ -0,0 +1,43 @@
/*
* Copyright 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.modifiers
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.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalAutofill
import androidx.compose.ui.platform.LocalAutofillTree
@Suppress("ModifierComposed")
@OptIn(ExperimentalComposeUiApi::class)
fun Modifier.autofill(autofillTypes: List<AutofillType>, onFill: (String) -> Unit) = composed {
val autofillNode = AutofillNode(autofillTypes, onFill = onFill)
LocalAutofillTree.current += autofillNode
val autofill = LocalAutofill.current
this
.onGloballyPositioned {
// Inform autofill framework of where our composable is so it can show the popup in the right place
autofillNode.boundingBox = it.boundsInWindow()
}
.onFocusChanged {
autofill?.run {
if (it.isFocused) {
requestAutofillForNode(autofillNode)
} else {
cancelAutofillForNode(autofillNode)
}
}
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright 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.modifiers
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
fun Modifier.onTabOrEnterKeyFocusNext(focusManager: FocusManager): Modifier = onPreviewKeyEvent { event ->
if (event.key == Key.Tab || event.key == Key.Enter) {
if (event.type == KeyEventType.KeyUp) {
focusManager.moveFocus(FocusDirection.Down)
}
true
} else {
false
}
}

View file

@ -15,25 +15,19 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.TextFieldColors import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.input.key.Key import androidx.compose.ui.text.TextRange
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp 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.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@ -42,7 +36,7 @@ import io.element.android.libraries.designsystem.utils.allBooleans
import io.element.android.libraries.designsystem.utils.asInt import io.element.android.libraries.designsystem.utils.asInt
@Composable @Composable
fun OutlinedTextField( fun FilledTextField(
value: String, value: String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -60,12 +54,16 @@ fun OutlinedTextField(
keyboardActions: KeyboardActions = KeyboardActions.Default, keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false, singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = OutlinedTextFieldDefaults.shape, shape: Shape = TextFieldDefaults.shape,
colors: TextFieldColors = OutlinedTextFieldDefaults.colors() colors: TextFieldColors = TextFieldDefaults.colors(
unfocusedContainerColor = ElementTheme.colors.bgSubtleSecondary,
focusedContainerColor = ElementTheme.colors.bgSubtleSecondary,
disabledContainerColor = ElementTheme.colors.bgSubtleSecondary,
errorContainerColor = ElementTheme.colors.bgSubtleSecondary,
)
) { ) {
androidx.compose.material3.OutlinedTextField( androidx.compose.material3.TextField(
value = value, value = value,
onValueChange = onValueChange, onValueChange = onValueChange,
modifier = modifier, modifier = modifier,
@ -83,7 +81,6 @@ fun OutlinedTextField(
keyboardActions = keyboardActions, keyboardActions = keyboardActions,
singleLine = singleLine, singleLine = singleLine,
maxLines = maxLines, maxLines = maxLines,
minLines = minLines,
interactionSource = interactionSource, interactionSource = interactionSource,
shape = shape, shape = shape,
colors = colors, colors = colors,
@ -91,7 +88,7 @@ fun OutlinedTextField(
} }
@Composable @Composable
fun OutlinedTextField( fun FilledTextField(
value: TextFieldValue, value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit, onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -108,12 +105,12 @@ fun OutlinedTextField(
keyboardOptions: KeyboardOptions = KeyboardOptions.Default, keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default, keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false, singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = OutlinedTextFieldDefaults.shape, shape: Shape = TextFieldDefaults.shape,
colors: TextFieldColors = OutlinedTextFieldDefaults.colors() colors: TextFieldColors = TextFieldDefaults.colors()
) { ) {
androidx.compose.material3.OutlinedTextField( androidx.compose.material3.TextField(
value = value, value = value,
onValueChange = onValueChange, onValueChange = onValueChange,
modifier = modifier, modifier = modifier,
@ -137,37 +134,62 @@ fun OutlinedTextField(
) )
} }
@OptIn(ExperimentalComposeUiApi::class) @Preview(group = PreviewGroup.TextFields)
fun Modifier.onTabOrEnterKeyFocusNext(focusManager: FocusManager): Modifier = onPreviewKeyEvent { event -> @Composable
if (event.key == Key.Tab || event.key == Key.Enter) { internal fun FilledTextFieldLightPreview() =
if (event.type == KeyEventType.KeyUp) { ElementPreviewLight { ContentToPreview() }
focusManager.moveFocus(FocusDirection.Down)
}
true
} else {
false
}
}
@Preview(group = PreviewGroup.TextFields) @Preview(group = PreviewGroup.TextFields)
@Composable @Composable
internal fun OutlinedTextFieldsPreview() = ElementPreviewLight { ContentToPreview() } internal fun FilledTextFieldDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Preview(group = PreviewGroup.TextFields)
@Composable
internal fun OutlinedTextFieldsDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
@ExcludeFromCoverage @ExcludeFromCoverage
@Composable
private fun ContentToPreview() { private fun ContentToPreview() {
Column(modifier = Modifier.padding(4.dp)) { Column(modifier = Modifier.padding(4.dp)) {
allBooleans.forEach { isError -> allBooleans.forEach { isError ->
allBooleans.forEach { enabled -> allBooleans.forEach { enabled ->
allBooleans.forEach { readonly -> allBooleans.forEach { readonly ->
OutlinedTextField( 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 = {}, onValueChange = {},
label = { Text(text = "label") }, label = { Text(text = "label") },
value = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}",
isError = isError, isError = isError,
enabled = enabled, enabled = enabled,
readOnly = readonly, readOnly = readonly,

View file

@ -7,36 +7,37 @@
package io.element.android.libraries.designsystem.theme.components package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.MutableInteractionSource 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.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.AutofillNode import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.autofill.AutofillType import androidx.compose.ui.text.TextLayoutResult
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.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme 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.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@ -44,56 +45,63 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.utils.allBooleans import io.element.android.libraries.designsystem.utils.allBooleans
import io.element.android.libraries.designsystem.utils.asInt import io.element.android.libraries.designsystem.utils.asInt
/**
* https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=2008-37137
*/
@Composable @Composable
fun TextField( fun TextField(
value: String, value: String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, label: String? = null,
readOnly: Boolean = false, supportingText: String? = null,
textStyle: TextStyle = LocalTextStyle.current, placeholder: String? = null,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null, leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null,
supportingText: @Composable (() -> Unit)? = null,
isError: Boolean = false, isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None, enabled: Boolean = true,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default, readOnly: Boolean = false,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false, singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, 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() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.shape, onTextLayout: (TextLayoutResult) -> Unit = {},
colors: TextFieldColors = TextFieldDefaults.colors(
unfocusedContainerColor = ElementTheme.colors.bgSubtleSecondary,
focusedContainerColor = ElementTheme.colors.bgSubtleSecondary,
disabledContainerColor = ElementTheme.colors.bgSubtleSecondary,
errorContainerColor = ElementTheme.colors.bgSubtleSecondary,
)
) { ) {
androidx.compose.material3.TextField( val isFocused by interactionSource.collectIsFocusedAsState()
BasicTextField(
value = value, value = value,
onValueChange = onValueChange, onValueChange = onValueChange,
modifier = modifier, modifier = modifier,
textStyle = textFieldStyle(enabled),
interactionSource = interactionSource,
enabled = enabled, 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, singleLine = singleLine,
maxLines = maxLines, maxLines = maxLines,
interactionSource = interactionSource, minLines = minLines,
shape = shape, readOnly = readOnly,
colors = colors, 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 @Composable
@ -101,69 +109,200 @@ fun TextField(
value: TextFieldValue, value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit, onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, label: String? = null,
readOnly: Boolean = false, supportingText: String? = null,
textStyle: TextStyle = LocalTextStyle.current, placeholder: String? = null,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null, leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null,
supportingText: @Composable (() -> Unit)? = null,
isError: Boolean = false, isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None, enabled: Boolean = true,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default, readOnly: Boolean = false,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false, singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, 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() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.shape, onTextLayout: (TextLayoutResult) -> Unit = {},
colors: TextFieldColors = TextFieldDefaults.colors()
) { ) {
androidx.compose.material3.TextField( val isFocused by interactionSource.collectIsFocusedAsState()
BasicTextField(
value = value, value = value,
onValueChange = onValueChange, onValueChange = onValueChange,
modifier = modifier, modifier = modifier,
textStyle = textFieldStyle(enabled),
interactionSource = interactionSource,
enabled = enabled, 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, singleLine = singleLine,
maxLines = maxLines, maxLines = maxLines,
interactionSource = interactionSource, minLines = minLines,
shape = shape, readOnly = readOnly,
colors = colors, 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) @Preview(group = PreviewGroup.TextFields)
@Composable @Composable
internal fun TextFieldLightPreview() = internal fun TextFieldsLightPreview() = ElementPreviewLight { ContentToPreview() }
ElementPreviewLight { ContentToPreview() }
@Preview(group = PreviewGroup.TextFields) @Preview(group = PreviewGroup.TextFields)
@Composable @Composable
internal fun TextFieldDarkPreview() = internal fun TextFieldsDarkPreview() = ElementPreviewDark { ContentToPreview() }
ElementPreviewDark { ContentToPreview() }
@ExcludeFromCoverage
@Composable @Composable
@ExcludeFromCoverage
private fun ContentToPreview() { private fun ContentToPreview() {
Column(modifier = Modifier.padding(4.dp)) { Column(modifier = Modifier.padding(4.dp)) {
allBooleans.forEach { isError -> allBooleans.forEach { isError ->
allBooleans.forEach { enabled -> allBooleans.forEach { enabled ->
allBooleans.forEach { readonly -> allBooleans.forEach { readonly ->
TextField( TextField(
onValueChange = {},
label = "Label",
value = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}", value = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}",
onValueChange = {}, supportingText = "Supporting text",
label = { Text(text = "label") },
isError = isError, isError = isError,
enabled = enabled, enabled = enabled,
readOnly = readonly, readOnly = readonly,
@ -174,62 +313,3 @@ private fun ContentToPreview() {
} }
} }
} }
@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") },
isError = isError,
enabled = enabled,
readOnly = readonly,
)
Spacer(modifier = Modifier.height(2.dp))
}
}
}
}
}
@Suppress("ModifierComposed")
@OptIn(ExperimentalComposeUiApi::class)
fun Modifier.autofill(autofillTypes: List<AutofillType>, onFill: (String) -> Unit) = composed {
val autofillNode = AutofillNode(autofillTypes, onFill = onFill)
LocalAutofillTree.current += autofillNode
val autofill = LocalAutofill.current
this
.onGloballyPositioned {
// Inform autofill framework of where our composable is so it can show the popup in the right place
autofillNode.boundingBox = it.boundsInWindow()
}
.onFocusChanged {
autofill?.run {
if (it.isFocused) {
requestAutofillForNode(autofillNode)
} else {
cancelAutofillForNode(autofillNode)
}
}
}
}

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c94e5e7c3b044017e6cb98985671875c7ab6103533e2a5e15aed17a01a52576b
size 30470

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5053e8716e3bfe5703f6d00e304101bbbf6cdebaf5eb5088a6043171a1f34e47
size 41328

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e6270f3b7935c47da76ce4e81aa7e8eac13a9340e7ccb198f8cd6956d746dfa8
size 59452

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f6a98ba9f5edc21b8781e84ccaf6f93c3608a4aa64464494304962d8cb62a102
size 31477

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:150b375f3fa9c180ec221b3fcc3ed0397b75920cb760e25e327fd6be46370618
size 41954

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c3b02b0bc0d557bac4d5aa945daf04d5d58dab44d9e4284d030804e6b5ed6a49
size 60703

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e7de44cc8f686264788aa79cde24b9896c6ca4dabc49b246abbd1d3dd0311e25
size 57891

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2df49a9c82438dbf1ba0ba0670cadd8e7bb47a7e25de27793fd756760cf7c5f2
size 79562

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2df49a9c82438dbf1ba0ba0670cadd8e7bb47a7e25de27793fd756760cf7c5f2
size 79562

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c754daf307554dc0c83ba0f89c38f5fa7f5c2edff82907b0826c2b781498dbcb
size 56047

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b6e43161abdccacbede4a8c4b4ff446026035df4be0894a55c6364681458bb2f
size 78805

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b6e43161abdccacbede4a8c4b4ff446026035df4be0894a55c6364681458bb2f
size 78805

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:74f12ea2c5114363b809fcf4d897487cb87ecfab361952471c05d22898d0048f oid sha256:a9d0338485aabe5296754868ce2fcd5097d914eea28298ec11c16f7161ae2341
size 130010 size 111617

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:6950f5e0824c964936077ecda4ff7edb8c8c6796b5a4ae304e6027e57a83cb55 oid sha256:88584b1d4a4876e68e2972f9bbd03156127ee9b552de1ec20a9057d8f6cb1828
size 116382 size 101296

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:afa896a21369e764e32b1dd4858a34708ddf38426a4a8aaa724761e76a90fed7 oid sha256:91cdcd831bc82480a130093a2f561bde8f0c3f0f2b9e7251628763202fc7bc39
size 35316 size 35249

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2dc060e53c33a7a06895e1be02f45f7f7e52b287d87b41c76d3321a71184f525 oid sha256:971e260530cfd5b25cce035272820cb067c7e4a4a34fa6992526e5176913ea2d
size 36501 size 36617

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1cdff1f6e189eff691125330b0f2c733ffd16fca324a5f2faee3dd15c9b7bc90 oid sha256:56fde0cdb5a5196c277c43a1b9909401f40a4e7ef8bfe2e4d80592f0768fe5d5
size 27119 size 27306

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:60e85a3e25fd112009111634ddc59a6ae7d884e26ea29e0598c1af10818abcb5 oid sha256:2db3dad746069968a83693752e985c25b72ca31be11b5c65d7da234f96d377d3
size 34285 size 34055

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9b518aa920d7183d740be4358dcc495e7df1c2d40c866addb728c7af8e273dbe oid sha256:61739bd834ee18df778b4737f38f656d4342397135d518791ff4518d8fdea96d
size 35502 size 35173

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a96833c5ba2508d77e1b8a1f885df78fb979fcb530223c4591076120729559e4 oid sha256:ae8a729e91657959dbac49ef1c0f41fe37c296923eef6c22bdc9692d389ca346
size 25080 size 25207

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:4054b601d6afc38467b632a19a5e12540b21dccbd67a885bcc13f8ad8a86636e oid sha256:a5419be4719399829da9684e6635873168f274120738e43c0b101603d8499da1
size 23532 size 23562

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:99fae3f838aa948b77391ef87d71ef56f2ac68b734bfdbd9c2b1868793dba192 oid sha256:cd128a94f904caabcd0557b2d7742e743fe80ad8618d96148dc469d7c0f3a006
size 51583 size 51651

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2426456773a27afb8daedb3d4ce8d03901d5b4c5a92c034537a79cd0ed018b0b oid sha256:d27d44214d8f083ee8ecbda76e060167af26f00052c9c0acb43b77e87432ae54
size 23067 size 23084

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b929faac1dc1e016ae59333941612231fdc56ceeba56515b28d7e768e7fce4e2 oid sha256:ecd37beb7a097aadda38238b36327dc5da809834f489c583146b9189d4e8a628
size 50630 size 50563

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:de520c3c0cf2133c2f862e061055b24d11a9fd7b54644b7aaaa989a8ecbe466f oid sha256:d8a8e7035c518edf069c9860e9fe4a894d144420541d6397066ec5ee7742df57
size 76719 size 77584

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:0ac8095520e4a80826f34733f90d1cb3d56601f194edbd890508a6fc6f31eadf oid sha256:2fc2fd1c67088e29a3d8e2dc633b411cd8256d821243d5d2f96bcf6b8e5ab6cc
size 75246 size 75979

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a9d2f18ee31a3370c08ba437ac21173538117b8fcdfeb45665677746c9fdd335 oid sha256:cd29b3483f20da49930f1bc65c04c25a5ba045a8cf7557ca65df26be3663d94a
size 60790 size 61388

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:59af9b4f6e390612464e8e83a1fbc0657a4bbd8c954c160e10c05bc6d48442fc oid sha256:e26369765383ad4e9d22dc0c0a2945fc28ebfcdc5f8f387dcebcc3db859de544
size 55191 size 55677

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d244fa646c87a6379d14eab9fa182652479473b5c77559e4631f3ffa19c0e1c4 oid sha256:cf8e762edb7915238e9f2dc3b71b32d44659a02e031b169d004eb15b497e45c4
size 52134 size 52747

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:babfe5dd3b5eaff3cddeb3a09b6cc72c86241510311bad2322d3938919dd6e01 oid sha256:d8bcd7f9287d275ee4fe2d9c8d6ef39715644696c8ddeaf84db7eb5877cce4c7
size 74907 size 75645

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:cca5bcbc56dcd6b47e4608682a0a343eb612a0185332e245ca0025336a09bcd0 oid sha256:1cc6acf5ef063b3bf4d101afe0c65435e2c4a68990ab3e860f57ad0bc18b32ec
size 73344 size 74073

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:88e989abce89da86ef8a7e2a4e036ac3ff9b41e38270edc3be9959a18cd4c86c oid sha256:d5fdc298a5f3320d57d5709f8d979b13712351d2b29e01be88f1ad43b9bb04bf
size 57191 size 57856

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9264c3aab735dc4546e9a12eb7698ad4572c911489906310044c8a5c65336a56 oid sha256:f32a16cdf84a60cc3d74fb86201355dbcae8ec3dbb4b95c1999e0106752299f9
size 52219 size 52817

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c7e9d33860371dd2a280175f9482e43589aeaf92f443e3d7cb7ab487cdef1dfb oid sha256:2132cd35e731279fba539e10b2843b1ae012d1db7e01653e2f0ec60c054e9061
size 48737 size 49369

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:85690981c628723b7ae353fe82277967dfda763953d0fca91e2575e3d8d84c25 oid sha256:24d232b144ff5664fecbd3c83fc04533eef8b3e82e4984b93f4156d996ad33e2
size 43788 size 43569

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d57f594ecf21d414ed0c301b30aa400ad3d04ca4aacae647899ae7550db7521a oid sha256:741ad1b881c9598c42c91603e84166768b5dc5a713ec191dc1c34c05360dbb92
size 45012 size 44908

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f81c06c3cbc14d8f9528a36f46ea9b39510f6eb9b1f93e731d4a182ed2a9dba8 oid sha256:0da11f0f4d9c191218e0d45caa37620db92311bbf126788a1ac1d84ca03b63a5
size 44549 size 44444

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:4fba92eab1bbfc9338db2581f8375c3edf09b3130e7997c1016cf0dcf782284c oid sha256:cc14af0383a159fbba7dc643e0d925db9ff8c4b22c2e38afcda20b504fd53d93
size 43209 size 44165

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fc2bdf0c222a688693e3df942b1c359f0e53799e1a81288f2a04a73bb53929c4 oid sha256:e96f672af0dd6a622deb18bfccea861cb51b63c3355e85ee8a8db6543e3de05c
size 34245 size 33803

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f81c06c3cbc14d8f9528a36f46ea9b39510f6eb9b1f93e731d4a182ed2a9dba8 oid sha256:0da11f0f4d9c191218e0d45caa37620db92311bbf126788a1ac1d84ca03b63a5
size 44549 size 44444

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f2801aecec234fa032e060262f030aebbf39cd23c9d4bfa014caddd422e93ffb oid sha256:31e50bed6ff94d2e20bb8cb0b9c35154de4c148c56a37518b0721fe463d7078b
size 42482 size 42242

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d0b38c4e39ce03f0fbdcb51425872a3ed096d58b5c16d05ced11bea882053c58 oid sha256:ca3973d21ea5575ab0921f78c4443c69c21f1f3f07ca78612c57bfe58b261002
size 43652 size 43748

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a26fb7c853e0dd27b48a5d9ef247067f6bd8f8668695949be592dca1e54d3634 oid sha256:f46c2d027a11f6dfca7edabc3d12ca7feed43c9688007c7cac9b654fb6fd6710
size 43311 size 43411

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c79d8c292de4b8dab500345a737274d84580f3d8c886652988c004363f975597 oid sha256:4b5219d31af9590df4e78041af6cf217cfadd7cbe67753b22e919758e4a31f53
size 42179 size 42608

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:02826a3e3870f8c64a91a1ef0662cf2aa17818481e1c1d5f6ac3512eadb614a5 oid sha256:85e3ccef2770f569ad2b062b9d7b86438b95e7235bf4d511621fa21dc2086f3e
size 32041 size 31504

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a26fb7c853e0dd27b48a5d9ef247067f6bd8f8668695949be592dca1e54d3634 oid sha256:f46c2d027a11f6dfca7edabc3d12ca7feed43c9688007c7cac9b654fb6fd6710
size 43311 size 43411

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1dfe32bea70bff2eb009cde05441a8476701366d34341468a96101c336630c3d oid sha256:88fbd2415816962033bc3a5798c2e75d3e6fffe2e2886072b67f9acb8643d1e2
size 32242 size 31586

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c34faf032e7f38b49d9bb7e99c8688f9e1c7fd821d951a26c52b2bb4bc9cf24d oid sha256:d1d455bd313f90002307e23823e14189bf53ebd0f0b1cfa4478514d2bb8ab7d0
size 36637 size 36440

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5199a5d1f36ee96a30c81dd328dee7c07fc96fd5eab8a4b93f2fe33ccef4405d oid sha256:470d70f74c6c5560f5324874b2cedd0458614e3bdf6c65356879eb42ff337bb2
size 38989 size 38892

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:3153a4e0b95bc3c6cd7c1424e864c9e1aadee95eee6a66fa03e760e9cd7cb10f oid sha256:cc85c3e76d57bc1b46a427ecee9ca64c238c419a346a43cd52d3a6e9f4199ed4
size 44462 size 43958

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:85a96fa375f9c77754feb70676a7aeb23803f9688bf3de6e61ebdf39062f42f1 oid sha256:3399b98bd9f8d3ba2175125656db8871ba14ec98f80595d7be95bf8438a33219
size 27219 size 26803

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:df844f42580ca97e481656b04056ab0ae02b32c83bb6d46411c9d60040b5cf86 oid sha256:d59fb94b1c8430065b7e40b413d250e631511acfac88cb01c24cf63e3d354cc1
size 117912 size 117561

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:96d2015cfd86eec443b94263da66c769df35135e8319d19bcab137a2b363d51a oid sha256:dd2845e9385c972347a1200ae3edb4d6a2bc866a4f4f34f07eaf9f96d0407405
size 32781 size 32270

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:7af8a815fbdf00bc72ee8bdadfeb2d328b28d0f5a25d845f195889af8abba25e oid sha256:98a74b997bf3c1e1365abf1c944453b15f1f8eb4ee48e24d91dda05c0a602b2a
size 32770 size 33245

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:16d14b57d5299dba1cec83f416d6283065281ab5a50f7cb72286d7cea3d2dff5 oid sha256:ed0cba1b78f970ab1ef5bfc277da6fcfbad8e07d3d3d796daab43948b0423f41
size 31067 size 30639

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:18713df330ee3b81cd015e09151e4b4a4739ddd3201a15f8d2e1286ee16b2d1e oid sha256:921d30810d6e1b6abfbd24bc7cef4b90916db0b41dccfd7de2298f81882fb5c7
size 35436 size 35224

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fef21564c727e0937748abcff1cd9ca45a54bfafe70176d7110f5321de9e7acf oid sha256:a07d6cc47b853a2f51aa8250290a5d618d702a40150bbae221bee9f2588828f9
size 36436 size 36271

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a218321a53ef189108aa19985bead154eb33afd4be5adf364ad0c0569bf003bf oid sha256:fe35cfe537168054a634faeb6c10519b2d13b694d532b152728df588c2f3649a
size 43054 size 42636

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fe01f18063cdaddfaa8f3d9c3395d5dba73c1726908b28e60a945d40a83c5978 oid sha256:58a98bf2a38cb65d90f8e6181b70b673078830182d9f506598df5bda75cfdfd7
size 26468 size 26089

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d7dccaaf3da114e97fdaf89b000fe62d1b8235402edd3d4027e970746057f154 oid sha256:4af7eec8cb1d89c71a9b2fe1efb28dbb956bac465e215ba7414432d7a37c272f
size 114209 size 113942

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:0885f4af4e3e8afd56aaeb44f517b75eb087d9f21e1095da2c8a3590715a8672 oid sha256:b5b1e6dbf5d662f0e4255dd0328bbe969ae6d309e4c9a5dbb6977671b940d428
size 31669 size 31310

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:cf1b3633e2a946b3a63c326cdad8ab14dfb4d1d5b41c06754924a696cde47ea5 oid sha256:31940f629ceaec427e44e1c4576d489550e40c652da5894bcca6da3f0642c5ff
size 30290 size 30708

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f1c5669f63f761311a49a7d82475ccd7463bdb5051db9add2875e13514272690 oid sha256:4362307104a54c1e65ffe79765b54ce9b11ea482f1965d6ec2a8642b023110a6
size 20189 size 20108

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fe8c7f7de0d56bfc60af6771fd076104687e1f123ced09dfdedc625c03431458 oid sha256:221e75077d503dc234772e3d3352055311e08290539f104e02c8a21463f03369
size 20361 size 20239

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b1a27216d9fb2e6d28109f6f414c0a06598103c69d182cdf5e926c930ac0a6b0 oid sha256:c19217fdb11145af9a2e86bbdb9fa203ac717e8e94ca61798b4861392bb396e1
size 69545 size 69416

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:97ddd11519803c8d042e593c0d231051bc9518b4fbe2f63d80792ea34319442f oid sha256:cf191b543c1b87e0c33965c215b842f05e0f07ab86a4fbc2b2acbd577dc4df17
size 87044 size 108891

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f6fd4487ae63d2116b70a31b5ee8d18135cf735f6257134afd6572b0dbe1ac5b oid sha256:905112d921f655c9a122261b4a1d1980ed16079611be0b20066d976780ea8c5c
size 63216 size 66019

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b1a27216d9fb2e6d28109f6f414c0a06598103c69d182cdf5e926c930ac0a6b0 oid sha256:c19217fdb11145af9a2e86bbdb9fa203ac717e8e94ca61798b4861392bb396e1
size 69545 size 69416

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1c2cd5fc3c6bef3f6cfc9f1d61bee2e2e41829ae3e77a3d8de31cf2168c25f61 oid sha256:c86dd4d1ababe8df2d373aa1b47c14bf71e4744de78bff3efdab2f9fcb5034da
size 52908 size 49211

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ca62ad16aa94477303c61f1796d6fff8262fde99c3b31184538f6ae9234dc950 oid sha256:1f7c6104e319a7246c04dcc4749c01e7147b412e6905e1fec70b7331632cf1a2
size 67807 size 67441

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f625b162bbd3172e953676219de6ba00e0cb34f0acbcda5ff6847359e7b867e3 oid sha256:2497fd0b7d7ae2e8da0a69e36a34c0dd7f7c8abb52b95005cd961b3c5c4b8734
size 85035 size 106454

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:23f505a91ebe3438f52c65f53362fc2d5cca57a8959e5459722217541ec24fc9 oid sha256:d84ab79382862503da8f18a54d05578a560f515f9062af5fd6c922fe830ca84a
size 62030 size 63656

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ca62ad16aa94477303c61f1796d6fff8262fde99c3b31184538f6ae9234dc950 oid sha256:1f7c6104e319a7246c04dcc4749c01e7147b412e6905e1fec70b7331632cf1a2
size 67807 size 67441

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:bbe73e23b4217cf10f3a5d50483bcd4d1fdc956b35129e66133c1c9ee26595bd oid sha256:3afd2de9146b6d29dec8e6249acbf771cc602d27f5783523da844f24f24c8daf
size 49536 size 45984

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e534ca2cc96a4b7a0e840eb18d153479f9e641348216fb4237c431dc8ef727d4 oid sha256:2450f444e0e4c7a0f354ff8a38225df5b0255280924996b0d4d40b72fdf0438f
size 27831 size 27737

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:31f588a58c0e79c37edbdcc72748fb1cd69e15e2313f48c5800fcf7f473d1f57 oid sha256:2633d18f133660a2daff4ba902abe0450cf233714d43f5de8897379784f082c5
size 21509 size 21375

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1e085b1745cd0445b7954af882a73b50bbd4dea74ec6def5d49562f6839c6095 oid sha256:4dbe0286f64a876af3bc4570141e26881462d8ca17485632e2fc599ae9eed563
size 29353 size 29037

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:54f82d574bca5f68787bc239ecd58cfcae94f1e6dd2f760e48dd033aba784df9 oid sha256:09f520d47af8427aa51e0a1938adab74c3433952a68c248e090a8f99454e39bf
size 53535 size 53466

Some files were not shown because too many files have changed in this diff Show more