-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #30 from YAPP-Github/feature/MZ-159-textfield
Feature/mz 159 Textfield Component
- Loading branch information
Showing
6 changed files
with
382 additions
and
7 deletions.
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
...src/main/java/com/susu/core/designsystem/component/textfield/PriceVisualTransformation.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.susu.core.designsystem.component.textfield | ||
|
||
import androidx.compose.ui.text.AnnotatedString | ||
import androidx.compose.ui.text.input.OffsetMapping | ||
import androidx.compose.ui.text.input.TransformedText | ||
import androidx.compose.ui.text.input.VisualTransformation | ||
import java.text.DecimalFormat | ||
|
||
class PriceVisualTransformation( | ||
private val postfix: String, | ||
) : VisualTransformation { | ||
override fun filter(text: AnnotatedString): TransformedText { | ||
val amount = text.text | ||
|
||
if (amount.isEmpty()) { | ||
return TransformedText(AnnotatedString(""), OffsetMapping.Identity) | ||
} | ||
|
||
val formatAmount = DecimalFormat("#,###").format(amount.toBigDecimal()) | ||
|
||
return TransformedText( | ||
text = AnnotatedString(formatAmount + postfix), | ||
offsetMapping = object : OffsetMapping { | ||
override fun originalToTransformed(offset: Int): Int { | ||
if (offset <= 1) return offset | ||
|
||
val entireCommaCount = if (amount.length % 3 == 0) amount.length / 3 - 1 else amount.length / 3 | ||
val sliceUntil = if (offset + entireCommaCount <= formatAmount.length) offset + entireCommaCount else formatAmount.length | ||
val commaBeforeOffsetCount = formatAmount.substring(0 until sliceUntil).count { it == ',' } | ||
|
||
return offset + commaBeforeOffsetCount | ||
} | ||
|
||
override fun transformedToOriginal(offset: Int): Int { | ||
return when (offset) { | ||
in 0..1 -> offset | ||
in 2 until formatAmount.length -> { | ||
val entireCommaCount = if (amount.length % 3 == 0) amount.length / 3 - 1 else amount.length / 3 | ||
val sliceUntil = if (offset + entireCommaCount <= formatAmount.length) offset + entireCommaCount else formatAmount.length | ||
val commaBeforeOffsetCount = formatAmount.substring(0 until sliceUntil).count { it == ',' } | ||
offset - commaBeforeOffsetCount | ||
} | ||
|
||
else -> amount.length | ||
} | ||
} | ||
}, | ||
) | ||
} | ||
} |
125 changes: 125 additions & 0 deletions
125
...esignsystem/src/main/java/com/susu/core/designsystem/component/textfield/SusuTextField.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package com.susu.core.designsystem.component.textfield | ||
|
||
import androidx.compose.foundation.interaction.MutableInteractionSource | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.text.BasicTextField | ||
import androidx.compose.foundation.text.KeyboardActions | ||
import androidx.compose.foundation.text.KeyboardOptions | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.Brush | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.graphics.SolidColor | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.text.TextStyle | ||
import androidx.compose.ui.text.input.KeyboardType | ||
import androidx.compose.ui.text.input.VisualTransformation | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import com.susu.core.designsystem.R | ||
import com.susu.core.designsystem.theme.Gray100 | ||
import com.susu.core.designsystem.theme.Gray40 | ||
import com.susu.core.designsystem.theme.SusuTheme | ||
|
||
@Composable | ||
fun SusuBasicTextField( | ||
modifier: Modifier = Modifier, | ||
text: String = "", | ||
onTextChange: (String) -> Unit = {}, | ||
placeholder: String = "", | ||
textColor: Color = Gray100, | ||
placeholderColor: Color = Gray40, | ||
enabled: Boolean = true, | ||
textStyle: TextStyle = SusuTheme.typography.title_xl, | ||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default, | ||
keyboardActions: KeyboardActions = KeyboardActions.Default, | ||
maxLines: Int = 1, | ||
minLines: Int = 1, | ||
visualTransformation: VisualTransformation = VisualTransformation.None, | ||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, | ||
cursorBrush: Brush = SolidColor(Color.Black), | ||
) { | ||
BasicTextField( | ||
modifier = modifier, | ||
value = text, | ||
onValueChange = onTextChange, | ||
enabled = enabled, | ||
textStyle = textStyle.copy(color = textColor), | ||
keyboardActions = keyboardActions, | ||
keyboardOptions = keyboardOptions, | ||
maxLines = maxLines, | ||
minLines = minLines, | ||
visualTransformation = visualTransformation, | ||
interactionSource = interactionSource, | ||
cursorBrush = cursorBrush, | ||
) { innerTextField -> | ||
if (text.isEmpty()) { | ||
Text( | ||
text = placeholder, | ||
style = textStyle.copy(color = placeholderColor), | ||
) | ||
} | ||
innerTextField() | ||
} | ||
} | ||
|
||
@Composable | ||
fun SusuPriceTextField( | ||
modifier: Modifier = Modifier, | ||
text: String = "", | ||
onTextChange: (String) -> Unit = {}, | ||
placeholder: String = "", | ||
textColor: Color = Gray100, | ||
placeholderColor: Color = Gray40, | ||
enabled: Boolean = true, | ||
textStyle: TextStyle = SusuTheme.typography.title_xl, | ||
keyboardActions: KeyboardActions = KeyboardActions.Default, | ||
maxLines: Int = 1, | ||
minLines: Int = 1, | ||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, | ||
cursorBrush: Brush = SolidColor(Color.Black), | ||
) { | ||
val moneyUnitString = stringResource(R.string.money_unit) | ||
SusuBasicTextField( | ||
modifier = modifier, | ||
text = text, | ||
onTextChange = onTextChange, | ||
placeholder = placeholder, | ||
textColor = textColor, | ||
placeholderColor = placeholderColor, | ||
enabled = enabled, | ||
textStyle = textStyle, | ||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), | ||
keyboardActions = keyboardActions, | ||
maxLines = maxLines, | ||
minLines = minLines, | ||
interactionSource = interactionSource, | ||
cursorBrush = cursorBrush, | ||
visualTransformation = PriceVisualTransformation(postfix = moneyUnitString), | ||
) | ||
} | ||
|
||
@Composable | ||
@Preview | ||
fun SusuBasicTextFieldPreview() { | ||
SusuTheme { | ||
var text by remember { mutableStateOf("") } | ||
var price by remember { mutableStateOf("") } | ||
Column { | ||
SusuBasicTextField( | ||
text = text, | ||
onTextChange = { text = it }, | ||
placeholder = "이름을 입력해주세요", | ||
) | ||
SusuPriceTextField( | ||
text = price, | ||
onTextChange = { price = it }, | ||
placeholder = "금액을 입력해주세요", | ||
) | ||
} | ||
} | ||
} |
174 changes: 174 additions & 0 deletions
174
...em/src/main/java/com/susu/core/designsystem/component/textfield/SusuUnderlineTextField.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package com.susu.core.designsystem.component.textfield | ||
|
||
import androidx.compose.foundation.interaction.MutableInteractionSource | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.width | ||
import androidx.compose.foundation.text.KeyboardActions | ||
import androidx.compose.foundation.text.KeyboardOptions | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.drawBehind | ||
import androidx.compose.ui.geometry.Offset | ||
import androidx.compose.ui.graphics.Brush | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.graphics.SolidColor | ||
import androidx.compose.ui.text.TextStyle | ||
import androidx.compose.ui.text.input.VisualTransformation | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import com.susu.core.designsystem.component.util.ClearIconButton | ||
import com.susu.core.designsystem.theme.Gray100 | ||
import com.susu.core.designsystem.theme.Gray30 | ||
import com.susu.core.designsystem.theme.Gray50 | ||
import com.susu.core.designsystem.theme.Red60 | ||
import com.susu.core.designsystem.theme.SusuTheme | ||
|
||
enum class SusuUnderlineTextFieldColor( | ||
val textColor: Color, | ||
val underlineColor: Color, | ||
val limitationColor: Color, | ||
val descriptionColor: Color, | ||
) { | ||
Inactive( | ||
textColor = Gray30, | ||
underlineColor = Gray50, | ||
limitationColor = Gray30, | ||
descriptionColor = Color.Unspecified, | ||
), | ||
Active( | ||
textColor = Gray100, | ||
underlineColor = Gray100, | ||
limitationColor = Gray30, | ||
descriptionColor = Color.Unspecified, | ||
), | ||
Error( | ||
textColor = Gray100, | ||
underlineColor = Red60, | ||
limitationColor = Red60, | ||
descriptionColor = Red60, | ||
), | ||
} | ||
|
||
data class SusuUnderlineTextFieldTextStyle( | ||
val contentTextStyle: TextStyle, | ||
val limitationTextStyle: TextStyle, | ||
val descriptionTextStyle: TextStyle, | ||
) | ||
|
||
object SusuUnderlineTextFieldDefault { | ||
val textStyle: @Composable () -> SusuUnderlineTextFieldTextStyle = { | ||
SusuUnderlineTextFieldTextStyle( | ||
contentTextStyle = SusuTheme.typography.title_l, | ||
limitationTextStyle = SusuTheme.typography.title_xs, | ||
descriptionTextStyle = SusuTheme.typography.text_xxs, | ||
) | ||
} | ||
} | ||
|
||
@Composable | ||
fun SusuUnderlineTextField( | ||
modifier: Modifier = Modifier, | ||
text: String = "", | ||
onTextChange: (String) -> Unit = {}, | ||
placeholder: String = "", | ||
placeholderColor: Color = Gray30, | ||
description: String? = null, | ||
isError: Boolean = false, | ||
lengthLimit: Int = 20, | ||
onClickClearIcon: () -> Unit = {}, | ||
textStyle: @Composable () -> SusuUnderlineTextFieldTextStyle = SusuUnderlineTextFieldDefault.textStyle, | ||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default, | ||
keyboardActions: KeyboardActions = KeyboardActions.Default, | ||
visualTransformation: VisualTransformation = VisualTransformation.None, | ||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, | ||
cursorBrush: Brush = SolidColor(Color.Black), | ||
) { | ||
val textFieldColor = when { | ||
isError -> SusuUnderlineTextFieldColor.Error | ||
text.isEmpty() -> SusuUnderlineTextFieldColor.Inactive | ||
else -> SusuUnderlineTextFieldColor.Active | ||
} | ||
|
||
with(textStyle()) { | ||
Column( | ||
modifier = modifier, | ||
verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_s), | ||
) { | ||
Row( | ||
modifier = Modifier | ||
.drawBehind { | ||
drawLine( | ||
color = textFieldColor.underlineColor, | ||
start = Offset(0f, size.height), | ||
end = Offset(size.width, size.height), | ||
strokeWidth = 1f, | ||
) | ||
} | ||
.padding(SusuTheme.spacing.spacing_xxs), | ||
verticalAlignment = Alignment.CenterVertically, | ||
) { | ||
SusuBasicTextField( | ||
modifier = Modifier.weight(1f), | ||
text = text, | ||
textColor = textFieldColor.textColor, | ||
onTextChange = onTextChange, | ||
placeholder = placeholder, | ||
placeholderColor = placeholderColor, | ||
textStyle = contentTextStyle, | ||
keyboardActions = keyboardActions, | ||
keyboardOptions = keyboardOptions, | ||
visualTransformation = visualTransformation, | ||
interactionSource = interactionSource, | ||
cursorBrush = cursorBrush, | ||
) | ||
if (text.isNotEmpty()) { | ||
Box(modifier = Modifier.padding(horizontal = SusuTheme.spacing.spacing_s)) { | ||
ClearIconButton(iconSize = 24.dp, onClick = onClickClearIcon) | ||
} | ||
} else { | ||
Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_xxs)) | ||
} | ||
Text( | ||
text = "${text.length}/$lengthLimit", | ||
style = limitationTextStyle.copy(color = textFieldColor.limitationColor), | ||
) | ||
} | ||
description?.let { descriptionText -> | ||
Text(text = descriptionText, style = descriptionTextStyle.copy(color = textFieldColor.descriptionColor)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Preview | ||
@Composable | ||
fun SusuUnderlineTextFieldPreview() { | ||
SusuTheme { | ||
var text by remember { mutableStateOf("") } | ||
Column { | ||
SusuUnderlineTextField( | ||
text = text, | ||
onTextChange = { text = it }, | ||
placeholder = "김수수", | ||
) | ||
SusuUnderlineTextField( | ||
text = text, | ||
onTextChange = { text = it }, | ||
placeholder = "김수수", | ||
isError = true, | ||
description = "에러 메세지를 입력하세요", | ||
) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.