design : updates TextField api

This commit is contained in:
ganfra
2024-11-08 11:54:57 +01:00
parent 612dfa4c9a
commit a4b83fe024

View File

@@ -23,13 +23,17 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -63,18 +67,16 @@ fun TextField2(
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
visualTransformation: VisualTransformation = VisualTransformation.None,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
onTextLayout: (TextLayoutResult) -> Unit = {},
modifier: Modifier = Modifier
) {
val interactionSource = remember { MutableInteractionSource() }
val isFocused by interactionSource.collectIsFocusedAsState()
BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
textStyle = ElementTheme.typography.fontBodyLgRegular.copy(
color = if (readOnly) ElementTheme.colors.textSecondary else ElementTheme.colors.textPrimary
),
textStyle = textFieldStyle(enabled),
interactionSource = interactionSource,
enabled = enabled,
singleLine = singleLine,
@@ -87,77 +89,202 @@ fun TextField2(
visualTransformation = visualTransformation,
onTextLayout = onTextLayout,
) { innerTextField ->
Column {
if (label != null) {
Text(
text = label,
color = ElementTheme.colors.textPrimary,
style = ElementTheme.typography.fontBodyMdRegular,
)
}
DecorationBox(
label = label,
readOnly = readOnly,
enabled = enabled,
isFocused = isFocused,
isError = isError,
leadingIcon = leadingIcon,
placeholder = placeholder,
isTextEmpty = value.isEmpty(),
innerTextField = innerTextField,
trailingIcon = trailingIcon,
supportingText = supportingText
)
}
}
@Composable
fun TextField2(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
label: String? = null,
supportingText: String? = null,
placeholder: String? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
isError: Boolean = false,
enabled: Boolean = true,
readOnly: Boolean = false,
singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
visualTransformation: VisualTransformation = VisualTransformation.None,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
onTextLayout: (TextLayoutResult) -> Unit = {},
modifier: Modifier = Modifier
) {
val isFocused by interactionSource.collectIsFocusedAsState()
BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
textStyle = textFieldStyle(enabled),
interactionSource = interactionSource,
enabled = enabled,
singleLine = singleLine,
maxLines = maxLines,
minLines = minLines,
readOnly = readOnly,
cursorBrush = SolidColor(ElementTheme.colors.textPrimary),
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
visualTransformation = visualTransformation,
onTextLayout = onTextLayout,
) { innerTextField ->
DecorationBox(
label = label,
readOnly = readOnly,
enabled = enabled,
isFocused = isFocused,
isError = isError,
leadingIcon = leadingIcon,
placeholder = placeholder,
isTextEmpty = value.text.isEmpty(),
innerTextField = innerTextField,
trailingIcon = trailingIcon,
supportingText = supportingText
)
}
}
@Composable
private fun DecorationBox(
label: String?,
enabled: Boolean,
readOnly: Boolean,
isFocused: Boolean,
isError: Boolean,
placeholder: String?,
isTextEmpty: Boolean,
supportingText: String?,
leadingIcon: @Composable (() -> Unit)?,
trailingIcon: @Composable (() -> Unit)?,
innerTextField: @Composable () -> Unit,
) {
Column {
if (label != null) {
Text(
text = label,
color = ElementTheme.colors.textPrimary,
style = ElementTheme.typography.fontBodyMdRegular,
)
Spacer(modifier = Modifier.height(8.dp))
Surface(
shape = RoundedCornerShape(4.dp),
border = if (readOnly) {
null
} else {
BorderStroke(
width = if (isFocused) 2.dp else 1.dp,
color = when {
isError -> ElementTheme.colors.borderCriticalPrimary
isFocused -> ElementTheme.colors.borderInteractiveHovered
else -> ElementTheme.colors.borderInteractiveSecondary
}
)
},
color = if (readOnly) ElementTheme.colors.bgSubtleSecondary else ElementTheme.colors.bgCanvasDefault,
) {
Row(modifier = Modifier.padding(16.dp)) {
if (leadingIcon != null) {
}
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 && value.isEmpty()) {
Text(
text = placeholder,
color = ElementTheme.colors.textPlaceholder,
style = ElementTheme.typography.fontBodyLgRegular,
)
}
innerTextField()
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,
)
}
if (trailingIcon != null) {
Spacer(modifier = Modifier.width(8.dp))
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))
Row(
verticalAlignment = Alignment.CenterVertically,
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,
)
}
}
}
if (supportingText != null) {
Spacer(modifier = Modifier.height(4.dp))
SupportingTextLayout(isError, supportingText)
}
}
}
@Composable
private fun TextFieldContainer(
enabled: Boolean,
readOnly: Boolean,
isFocused: Boolean,
isError: Boolean,
content: @Composable () -> Unit
) {
Surface(
shape = RoundedCornerShape(4.dp),
border = if (readOnly) {
null
} else {
BorderStroke(
width = if (isFocused) 2.dp else 1.dp,
color = when {
!enabled -> ElementTheme.colors.borderDisabled
isError -> ElementTheme.colors.borderCriticalPrimary
isFocused -> ElementTheme.colors.borderInteractiveHovered
else -> ElementTheme.colors.borderInteractiveSecondary
}
)
},
color = when {
readOnly -> ElementTheme.colors.bgSubtleSecondary
!enabled -> ElementTheme.colors.bgCanvasDisabled
else -> ElementTheme.colors.bgCanvasDefault
},
content = content
)
}
@Composable
private fun SupportingTextLayout(isError: Boolean, supportingText: String) {
Row(horizontalArrangement = spacedBy(4.dp)) {
if (isError) {
Icon(
imageVector = CompoundIcons.Error(),
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = ElementTheme.colors.iconCriticalPrimary
)
}
Text(
text = supportingText,
color = if (isError) ElementTheme.colors.textCriticalPrimary else ElementTheme.colors.textSecondary,
style = ElementTheme.typography.fontBodySmRegular,
)
}
}
@Composable
private fun textFieldStyle(enabled: Boolean): TextStyle {
return ElementTheme.typography.fontBodyLgRegular.copy(
color = if (enabled) {
ElementTheme.colors.textPrimary
} else {
ElementTheme.colors.textSecondary
}
)
}
@Preview(group = PreviewGroup.TextFields)
@Composable
internal fun TextFields2LightPreview() = ElementPreviewLight { ContentToPreview() }