From ca2d95851f09d1c14a2e315db8296dbe2b42c6a9 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:33:16 +0900 Subject: [PATCH 01/14] =?UTF-8?q?[TNT-309]=20feat:=20=EC=8B=A0=EA=B7=9C=20?= =?UTF-8?q?TextField=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/textfield/TextField_2.kt | 241 ++++++++++++++++++ .../textfield/model/TextFieldSize.kt | 16 ++ .../textfield/model/TextFieldType.kt | 40 +++ 3 files changed, 297 insertions(+) create mode 100644 core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField_2.kt create mode 100644 core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/model/TextFieldSize.kt create mode 100644 core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/model/TextFieldType.kt diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField_2.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField_2.kt new file mode 100644 index 00000000..9f884117 --- /dev/null +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField_2.kt @@ -0,0 +1,241 @@ +package co.kr.tnt.designsystem.component.textfield + +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +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.RowScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +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.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +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.draw.clip +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize +import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldType +import co.kr.tnt.designsystem.theme.TnTTheme + +@Composable +fun TnTTextField2( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + placeholder: String = "", + size: TnTTextFieldSize = TnTTextFieldSize.SMALL, + isWarning: Boolean = false, + isEnable: Boolean = true, + keyboardType: KeyboardType = KeyboardType.Text, + focusRequester: FocusRequester? = null, + requestFocusOnStart: Boolean = false, + onClick: (() -> Unit)? = null, + trailing: @Composable (RowScope.() -> Unit)? = null, +) { + val focusManager = LocalFocusManager.current + var hasFocus by remember { mutableStateOf(false) } + val actualFocusRequester = focusRequester ?: remember { FocusRequester() } + + val type = TnTTextFieldType.from( + isWarning = isWarning, + hasFocus = hasFocus, + ) + val shape = RoundedCornerShape(8.dp) + + var textFieldValueState by remember { + mutableStateOf( + TextFieldValue( + text = value, + selection = TextRange(value.length), + ), + ) + } + + LaunchedEffect(value) { + if (textFieldValueState.text != value) { + textFieldValueState = TextFieldValue( + text = value, + selection = TextRange(value.length), + ) + } + } + + LaunchedEffect(requestFocusOnStart) { + if (requestFocusOnStart) { + actualFocusRequester.requestFocus() + } + } + + BasicTextField( + value = textFieldValueState, + onValueChange = { newValue -> + textFieldValueState = newValue + onValueChange(newValue.text) + }, + modifier = modifier + .fillMaxWidth() + .heightIn(min = size.minHeight) + .clip(shape) + .border( + width = type.borderWidth, + color = type.borderColor, + shape = shape, + ) + .clickable( + enabled = onClick != null, + onClick = { onClick?.invoke() }, + ) + .focusRequester(actualFocusRequester) + .onFocusChanged { focusState -> + if (focusState.isFocused && !hasFocus) { + // 포커스 획득 시 커서 우측 종단 이동 처리 + textFieldValueState = textFieldValueState.copy( + selection = TextRange(textFieldValueState.text.length), + ) + } + hasFocus = focusState.isFocused + }, + enabled = isEnable, + textStyle = TnTTheme.typography.body1Medium.copy( + color = TnTTheme.colors.neutralColors.Neutral800, + ), + keyboardOptions = KeyboardOptions( + keyboardType = keyboardType, + ), + keyboardActions = KeyboardActions( + onDone = { focusManager.clearFocus() }, + ), + singleLine = size == TnTTextFieldSize.SMALL, + cursorBrush = SolidColor(TnTTheme.colors.neutralColors.Neutral900), + decorationBox = { innerTextField -> + Row { + Box( + modifier = Modifier + .weight(1f) + .padding( + vertical = 12.dp, + horizontal = 16.dp, + ), + ) { + if (textFieldValueState.text.isEmpty() && placeholder.isNotEmpty()) { + Text( + text = placeholder, + style = TnTTheme.typography.body1Medium, + color = TnTTheme.colors.neutralColors.Neutral400, + ) + } + innerTextField() + } + trailing?.invoke(this@Row) + } + }, + ) +} + +private data class TextFieldPreviewState( + val name: String, + val value: String, + val isWarning: Boolean = false, + val isEnable: Boolean = true, + val size: TnTTextFieldSize = TnTTextFieldSize.SMALL, + val requestFocus: Boolean = false, +) + +private class TextFieldStateProvider : PreviewParameterProvider { + override val values = sequenceOf( + TextFieldPreviewState( + name = "Empty", + value = "", + ), + TextFieldPreviewState( + name = "Default", + value = "입력된 텍스트", + ), + TextFieldPreviewState( + name = "Focused", + value = "포커스된 상태", + requestFocus = true, + ), + TextFieldPreviewState( + name = "Warning", + value = "잘못된 입력", + isWarning = true, + ), + TextFieldPreviewState( + name = "Disabled", + value = "비활성화된 필드", + isEnable = false, + ), + TextFieldPreviewState( + name = "Large size - Default", + value = """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi, + ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit. + """.trimIndent(), + size = TnTTextFieldSize.LARGE, + ), + TextFieldPreviewState( + name = "Large size - Error", + value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + size = TnTTextFieldSize.LARGE, + isWarning = true, + ), + TextFieldPreviewState( + name = "Large size - Focused", + value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + size = TnTTextFieldSize.LARGE, + requestFocus = true, + ), + ) +} + +@Preview(name = "TextField", showBackground = true) +@Composable +private fun TnTTextField2Preview( + @PreviewParameter(TextFieldStateProvider::class) state: TextFieldPreviewState, +) { + TnTTheme { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + text = state.name, + style = TnTTheme.typography.body2Medium, + color = TnTTheme.colors.neutralColors.Neutral600, + ) + TnTTextField2( + value = state.value, + onValueChange = {}, + size = state.size, + isWarning = state.isWarning, + isEnable = state.isEnable, + requestFocusOnStart = state.requestFocus, + ) + } + } +} diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/model/TextFieldSize.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/model/TextFieldSize.kt new file mode 100644 index 00000000..d2ecc840 --- /dev/null +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/model/TextFieldSize.kt @@ -0,0 +1,16 @@ +package co.kr.tnt.designsystem.component.textfield.model + +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +enum class TnTTextFieldSize { + SMALL, + LARGE, + ; + + val minHeight: Dp + get() = when (this) { + SMALL -> Dp.Unspecified + LARGE -> 128.dp + } +} diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/model/TextFieldType.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/model/TextFieldType.kt new file mode 100644 index 00000000..dc19167f --- /dev/null +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/model/TextFieldType.kt @@ -0,0 +1,40 @@ +package co.kr.tnt.designsystem.component.textfield.model + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import co.kr.tnt.designsystem.theme.TnTTheme + +internal enum class TnTTextFieldType { + DEF, + FOCUS, + ERROR, + ; + + val borderColor: Color + @Composable + get() = when (this) { + DEF -> TnTTheme.colors.neutralColors.Neutral300 + FOCUS -> TnTTheme.colors.neutralColors.Neutral900 + ERROR -> TnTTheme.colors.redColors.Red500 + } + + val borderWidth: Dp + get() = when (this) { + DEF -> 1.dp + FOCUS -> 2.dp + ERROR -> 2.dp + } + + internal companion object { + fun from( + isWarning: Boolean, + hasFocus: Boolean, + ): TnTTextFieldType = when { + isWarning -> ERROR + hasFocus -> FOCUS + else -> DEF + } + } +} From 8d1a8d62809d2e88ed022f6a33e4c9c34a5ab891 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:20:20 +0900 Subject: [PATCH 02/14] =?UTF-8?q?[TNT-309]=20feat:=20=EC=8B=A0=EA=B7=9C=20?= =?UTF-8?q?LabeledTextField=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/textfield/LabeledTextField.kt | 372 ++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt new file mode 100644 index 00000000..3e06ea0a --- /dev/null +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt @@ -0,0 +1,372 @@ +package co.kr.tnt.designsystem.component.textfield + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +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.focus.FocusRequester +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import co.kr.tnt.core.designsystem.R +import co.kr.tnt.designsystem.component.button.TnTTextButton +import co.kr.tnt.designsystem.component.button.model.ButtonSize +import co.kr.tnt.designsystem.component.button.model.ButtonType +import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize +import co.kr.tnt.designsystem.theme.TnTTheme + +@Composable +fun TnTLabeledTextField2( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + title: String? = null, + showRequiredTitleBadge: Boolean = false, + placeholder: String = "", + size: TnTTextFieldSize = TnTTextFieldSize.SMALL, + isWarning: Boolean = false, + isEnable: Boolean = true, + warningMessage: String? = null, + maxLength: Int? = null, + keyboardType: KeyboardType = KeyboardType.Text, + focusRequester: FocusRequester? = null, + requestFocusOnStart: Boolean = false, + onClickTextField: (() -> Unit)? = null, + trailing: @Composable (RowScope.() -> Unit)? = null, +) { + Column(modifier = modifier.fillMaxWidth()) { + if (title != null) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = title, + style = TnTTheme.typography.body1Bold, + color = TnTTheme.colors.neutralColors.Neutral900, + ) + if (showRequiredTitleBadge) { + Text( + text = "*", + style = TnTTheme.typography.body1Bold, + color = TnTTheme.colors.redColors.Red500, + ) + } + } + Spacer(modifier = Modifier.height(8.dp)) + } + TnTTextField2( + value = value, + onValueChange = onValueChange, + modifier = Modifier.fillMaxWidth(), + placeholder = placeholder, + size = size, + isWarning = isWarning, + isEnable = isEnable, + keyboardType = keyboardType, + focusRequester = focusRequester, + requestFocusOnStart = requestFocusOnStart, + trailing = trailing, + onClick = onClickTextField, + ) + if ((isWarning && warningMessage != null) || maxLength != null) { + Spacer(modifier = Modifier.height(6.dp)) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth(), + ) { + if (isWarning && warningMessage != null) { + Text( + text = warningMessage, + style = TnTTheme.typography.body2Medium, + color = TnTTheme.colors.redColors.Red500, + modifier = Modifier.padding(top = 6.dp), + ) + } + if (maxLength != null) { + Spacer(modifier = Modifier.weight(1f)) + Text( + text = stringResource(R.string.text_counter, value.length, maxLength), + style = TnTTheme.typography.label1Medium, + color = when { + isWarning -> TnTTheme.colors.redColors.Red500 + else -> TnTTheme.colors.neutralColors.Neutral400 + }, + ) + } + } + } + } +} + +@Composable +fun TnTLabeledTextFieldWithTextButton( + value: String, + onValueChange: (String) -> Unit, + trailingButtonTitle: String, + onClickTrailingButton: () -> Unit, + modifier: Modifier = Modifier, + title: String? = null, + showRequiredTitleBadge: Boolean = false, + placeholder: String = "", + size: TnTTextFieldSize = TnTTextFieldSize.SMALL, + isWarning: Boolean = false, + isEnable: Boolean = true, + warningMessage: String? = null, + maxLength: Int? = null, + keyboardType: KeyboardType = KeyboardType.Text, + focusRequester: FocusRequester? = null, + requestFocusOnStart: Boolean = false, + trailingButtonSize: ButtonSize = ButtonSize.Small, + trailingButtonType: ButtonType = ButtonType.Primary, + trailingButtonEnabled: Boolean = true, + onClickTextField: (() -> Unit)? = null, +) { + TnTLabeledTextField2( + value = value, + onValueChange = onValueChange, + modifier = modifier, + title = title, + showRequiredTitleBadge = showRequiredTitleBadge, + placeholder = placeholder, + size = size, + isWarning = isWarning, + isEnable = isEnable, + warningMessage = warningMessage, + maxLength = maxLength, + trailing = { + TnTTextButton( + modifier = Modifier + .align(Alignment.CenterVertically) + .heightIn(max = trailingButtonSize.height) + .padding(end = 8.dp), + text = trailingButtonTitle, + size = trailingButtonSize, + type = trailingButtonType, + enabled = trailingButtonEnabled, + onClick = onClickTrailingButton, + ) + }, + keyboardType = keyboardType, + focusRequester = focusRequester, + requestFocusOnStart = requestFocusOnStart, + onClickTextField = onClickTextField, + ) +} + +@Preview(name = "Labeled TextField - Empty", showBackground = true) +@Composable +private fun TnTLabeledTextField2EmptyPreview() { + TnTTheme { + TnTLabeledTextField2( + title = "이름", + value = "", + onValueChange = {}, + modifier = Modifier.padding(16.dp), + ) + } +} + +@Preview(name = "Labeled TextField - With text", showBackground = true) +@Composable +private fun TnTLabeledTextField2WithTextPreview() { + TnTTheme { + TnTLabeledTextField2( + title = "이름", + value = "홍길동", + onValueChange = {}, + modifier = Modifier.padding(16.dp), + ) + } +} + +@Preview(name = "Labeled TextField - Required", showBackground = true) +@Composable +private fun TnTLabeledTextField2RequiredPreview() { + TnTTheme { + TnTLabeledTextField2( + title = "이름", + value = "", + onValueChange = {}, + showRequiredTitleBadge = true, + modifier = Modifier.padding(16.dp), + ) + } +} + +@Preview(name = "Labeled TextField - Error", showBackground = true) +@Composable +private fun TnTLabeledTextField2ErrorPreview() { + TnTTheme { + TnTLabeledTextField2( + title = "이름", + value = "잘못된 입력", + onValueChange = {}, + isWarning = true, + showRequiredTitleBadge = true, + warningMessage = "올바른 이름을 입력해주세요", + modifier = Modifier.padding(16.dp), + ) + } +} + +@Preview(name = "Labeled TextField - Large size", showBackground = true) +@Composable +private fun TnTLabeledTextField2LargePreview() { + TnTTheme { + TnTLabeledTextField2( + title = "메모", + value = "여러 줄의 텍스트를\n입력할 수 있습니다.", + onValueChange = {}, + size = TnTTextFieldSize.LARGE, + modifier = Modifier.padding(16.dp), + ) + } +} + +@Preview(name = "Labeled TextField - With Counter", showBackground = true) +@Composable +private fun TnTLabeledTextField2WithCounterEmptyPreview() { + TnTTheme { + var text by remember { mutableStateOf("") } + + TnTLabeledTextField2( + title = "닉네임", + value = text, + onValueChange = { text = it }, + maxLength = 10, + modifier = Modifier.padding(16.dp), + ) + } +} + +@Preview(name = "Labeled TextField - Counter + Required", showBackground = true) +@Composable +private fun TnTLabeledTextField2WithCounterTextPreview() { + TnTTheme { + var text by remember { mutableStateOf("홍길동") } + + TnTLabeledTextField2( + title = "닉네임", + value = text, + onValueChange = { text = it }, + maxLength = 10, + showRequiredTitleBadge = true, + modifier = Modifier.padding(16.dp), + ) + } +} + +@Preview(name = "Labeled TextField - Counter + Error", showBackground = true) +@Composable +private fun TnTLabeledTextField2WithCounterErrorPreview() { + TnTTheme { + var text by remember { mutableStateOf("매우 긴 닉네임 매우 긴 닉네임") } + + TnTLabeledTextField2( + title = "닉네임", + value = text, + onValueChange = { text = it }, + maxLength = 10, + isWarning = true, + showRequiredTitleBadge = true, + warningMessage = "10자 이내로 입력해주세요", + modifier = Modifier.padding(16.dp), + ) + } +} + +@Preview(name = "Labeled TextField - With Trailing Button", showBackground = true) +@Composable +private fun TnTLabeledTextField2WithTrailingButtonPreview() { + TnTTheme { + TnTLabeledTextFieldWithTextButton( + title = "닉네임", + value = "닉네임", + onValueChange = {}, + trailingButtonTitle = "중복확인", + onClickTrailingButton = { }, + trailingButtonSize = ButtonSize.Small, + modifier = Modifier.padding(16.dp), + ) + } +} + +@Preview(name = "All States Overview", showBackground = true, heightDp = 1000) +@Composable +private fun TnTLabeledTextField2AllStatesPreview() { + TnTTheme { + Column( + modifier = Modifier + .padding(16.dp) + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + Text("Basic", style = TnTTheme.typography.body1Bold) + TnTLabeledTextField2( + title = "이름", + value = "홍길동", + onValueChange = {}, + ) + + Text("Required", style = TnTTheme.typography.body1Bold) + TnTLabeledTextField2( + title = "이메일", + value = "", + onValueChange = {}, + showRequiredTitleBadge = true, + ) + + Text("Error with message", style = TnTTheme.typography.body1Bold) + TnTLabeledTextField2( + title = "전화번호", + value = "잘못된 형식", + onValueChange = {}, + isWarning = true, + showRequiredTitleBadge = true, + warningMessage = "올바른 전화번호를 입력해주세요", + ) + + Text("With Counter", style = TnTTheme.typography.body1Bold) + TnTLabeledTextField2( + title = "닉네임", + value = "홍길동", + onValueChange = {}, + maxLength = 10, + showRequiredTitleBadge = true, + ) + + Text("With Trailing Button", style = TnTTheme.typography.body1Bold) + TnTLabeledTextFieldWithTextButton( + title = "닉네임", + value = "안녕하세요. 우리는 YAPP TnT 팀이에요. 반갑습니다.", + onValueChange = {}, + trailingButtonTitle = "중복확인", + onClickTrailingButton = {}, + trailingButtonSize = ButtonSize.Small, + ) + + Text("Large size", style = TnTTheme.typography.body1Bold) + TnTLabeledTextField2( + title = "메모", + value = "여러 줄의\n텍스트 입력", + onValueChange = {}, + size = TnTTextFieldSize.LARGE, + ) + } + } +} From 0a47f1c191dc56c54379a91ea775609f2192c848 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:20:55 +0900 Subject: [PATCH 03/14] =?UTF-8?q?[TNT-309]=20feat:=20=EC=8B=A0=EA=B7=9C=20?= =?UTF-8?q?SelectableTextField=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../textfield/SelectableTextField_2.kt | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField_2.kt diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField_2.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField_2.kt new file mode 100644 index 00000000..ab4d4606 --- /dev/null +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField_2.kt @@ -0,0 +1,205 @@ +package co.kr.tnt.designsystem.component.textfield + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import co.kr.tnt.core.designsystem.R +import co.kr.tnt.designsystem.component.button.TnTTextButton +import co.kr.tnt.designsystem.component.button.model.ButtonSize +import co.kr.tnt.designsystem.component.button.model.ButtonType +import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize +import co.kr.tnt.designsystem.theme.TnTTheme + +@Composable +fun TnTSelectableTextField2( + value: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + placeholder: String = "", + size: TnTTextFieldSize = TnTTextFieldSize.SMALL, + isWarning: Boolean = false, + shouldClearFocus: Boolean = false, + trailing: @Composable (RowScope.() -> Unit)? = null, +) { + val focusManager = LocalFocusManager.current + + LaunchedEffect(shouldClearFocus) { + if (shouldClearFocus) { + focusManager.clearFocus() + } + } + + TnTTextField2( + value = value, + onValueChange = { }, + modifier = modifier, + placeholder = placeholder, + size = size, + isWarning = isWarning, + isEnable = false, + trailing = trailing ?: { + DefaultTrailingIcon( + modifier = Modifier + .align(Alignment.CenterVertically) + .padding(end = 8.dp), + ) + }, + onClick = onClick, + ) +} + +@Composable +fun TnTSelectableLabeledTextField2( + value: String, + onClickTextField: () -> Unit, + modifier: Modifier = Modifier, + title: String? = null, + showRequiredTitleBadge: Boolean = false, + placeholder: String = "", + size: TnTTextFieldSize = TnTTextFieldSize.SMALL, + isWarning: Boolean = false, + warningMessage: String? = null, + trailing: @Composable (RowScope.() -> Unit)? = null, +) { + TnTLabeledTextField2( + value = value, + onValueChange = { }, + modifier = modifier, + onClickTextField = onClickTextField, + title = title, + showRequiredTitleBadge = showRequiredTitleBadge, + placeholder = placeholder, + size = size, + isWarning = isWarning, + isEnable = false, + warningMessage = warningMessage, + trailing = trailing ?: { + DefaultTrailingIcon( + modifier = Modifier + .align(Alignment.CenterVertically) + .padding(end = 8.dp), + ) + }, + ) +} + +@Composable +fun TnTSelectableLabeledTextFieldWithTextButton( + value: String, + onClickTextField: () -> Unit, + trailingButtonTitle: String, + onClickTrailingButton: () -> Unit, + modifier: Modifier = Modifier, + title: String? = null, + showRequiredTitleBadge: Boolean = false, + placeholder: String = "", + size: TnTTextFieldSize = TnTTextFieldSize.SMALL, + isWarning: Boolean = false, + warningMessage: String? = null, + trailingButtonSize: ButtonSize = ButtonSize.Small, + trailingButtonType: ButtonType = ButtonType.Primary, +) { + TnTSelectableLabeledTextField2( + value = value, + onClickTextField = onClickTextField, + modifier = modifier, + title = title, + showRequiredTitleBadge = showRequiredTitleBadge, + placeholder = placeholder, + size = size, + isWarning = isWarning, + warningMessage = warningMessage, + trailing = { + TnTTextButton( + modifier = Modifier + .align(Alignment.CenterVertically) + .heightIn(max = trailingButtonSize.height) + .padding(end = 8.dp), + text = trailingButtonTitle, + size = trailingButtonSize, + type = trailingButtonType, + onClick = onClickTrailingButton, + ) + }, + ) +} + +@Composable +private fun DefaultTrailingIcon( + modifier: Modifier, +) { + Icon( + modifier = modifier, + painter = painterResource(R.drawable.ic_arrow_down), + contentDescription = null, + tint = TnTTheme.colors.neutralColors.Neutral400, + ) +} + +@Preview(name = "All States Overview", showBackground = true, heightDp = 800) +@Composable +private fun TnTSelectableTextField2AllStatesPreview() { + TnTTheme { + Column( + modifier = Modifier + .padding(16.dp) + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + Text("Empty", style = TnTTheme.typography.body1Bold) + TnTSelectableTextField2( + value = "", + onClick = {}, + placeholder = "선택해주세요", + ) + + Text("Selected", style = TnTTheme.typography.body1Bold) + TnTSelectableTextField2( + value = "2025/01/15", + onClick = {}, + ) + + Text("Labeled - Required", style = TnTTheme.typography.body1Bold) + TnTSelectableLabeledTextField2( + value = "", + onClickTextField = {}, + title = "날짜", + showRequiredTitleBadge = true, + placeholder = "2025/01/15", + ) + + Text("Labeled - Selected", style = TnTTheme.typography.body1Bold) + TnTSelectableLabeledTextField2( + value = "09:00", + onClickTextField = {}, + title = "시간", + showRequiredTitleBadge = true, + ) + + Text("Labeled - Warning", style = TnTTheme.typography.body1Bold) + TnTSelectableLabeledTextField2( + value = "", + onClickTextField = {}, + title = "식사 유형", + showRequiredTitleBadge = true, + placeholder = "선택해주세요", + isWarning = true, + warningMessage = "식사 유형을 선택해주세요", + ) + } + } +} From 1408f96465cace259a617ddbcbbf53d776fd18d4 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:21:50 +0900 Subject: [PATCH 04/14] =?UTF-8?q?[TNT-309]=20refactor:=20=EC=8B=A0?= =?UTF-8?q?=EA=B7=9C=20TextField=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/tnt/trainee/connect/CodeEntryPage.kt | 49 ++-- .../tnt/trainee/connect/PTSessionFormPage.kt | 264 +++++++++--------- .../connect/component/CodeTextField.kt | 159 ----------- .../mealrecord/TraineeMealRecordContract.kt | 2 - .../mealrecord/TraineeMealRecordScreen.kt | 25 +- .../mealrecord/TraineeMealRecordViewModel.kt | 3 - .../trainee/signup/TraineeBasicInfoPage.kt | 128 +++------ .../trainee/signup/TraineeProfileSetupPage.kt | 9 +- .../addptsession/AddPtSessionScreen.kt | 75 ++--- .../tnt/trainer/invite/TrainerInviteScreen.kt | 66 +---- .../modifymyinfo/TrainerModifyMyInfo.kt | 10 +- .../trainer/signup/TrainerProfileSetupPage.kt | 9 +- 12 files changed, 272 insertions(+), 527 deletions(-) delete mode 100644 feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/component/CodeTextField.kt diff --git a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/CodeEntryPage.kt b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/CodeEntryPage.kt index 23342d38..ca54fc88 100644 --- a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/CodeEntryPage.kt +++ b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/CodeEntryPage.kt @@ -27,12 +27,10 @@ import co.kr.tnt.designsystem.component.TnTIconPopupDialog import co.kr.tnt.designsystem.component.TnTTopBar import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton -import co.kr.tnt.designsystem.component.button.TnTTextButton -import co.kr.tnt.designsystem.component.button.model.ButtonSize +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextFieldWithTextButton import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.feature.trainee.connect.R import co.kr.tnt.navigation.model.ScreenMode -import co.kr.tnt.trainee.connect.component.CodeTextField import co.kr.tnt.trainee.connect.model.InputState import co.kr.tnt.ui.extensions.clearFocusOnTap import co.kr.tnt.core.designsystem.R as uiResource @@ -68,6 +66,7 @@ internal fun CodeEntryPage( onBackClick = onClickBack, ) } + ScreenMode.SKIP -> { TnTTopBar( title = stringResource(core_connect), @@ -81,6 +80,7 @@ internal fun CodeEntryPage( }, ) } + ScreenMode.CLOSE -> { TnTTopBar( title = stringResource(core_connect), @@ -111,20 +111,37 @@ internal fun CodeEntryPage( modifier = Modifier.padding(horizontal = 24.dp), ) Spacer(Modifier.padding(top = 48.dp)) - CodeTextField( - value = inviteCode, - onValueChange = onChangeInviteCode, - modifier = Modifier.padding(horizontal = 20.dp), - isCodeValid = inputState, - trailingComponent = { - TnTTextButton( - text = stringResource(R.string.verification), - size = ButtonSize.Small, - enabled = inviteCode.isNotBlank(), - onClick = { onClickValidate(inviteCode) }, + Column { + TnTLabeledTextFieldWithTextButton( + value = inviteCode, + onValueChange = onChangeInviteCode, + trailingButtonTitle = stringResource(R.string.verification), + onClickTrailingButton = { onClickValidate(inviteCode) }, + modifier = Modifier + .padding(horizontal = 20.dp), + title = stringResource(R.string.my_invite_code), + showRequiredTitleBadge = true, + placeholder = stringResource(R.string.enter_the_code), + isWarning = inputState == InputState.INVALID, + warningMessage = if (inputState == InputState.INVALID) { + stringResource(R.string.verification_fail) + } else { + null + }, + trailingButtonEnabled = inviteCode.isNotBlank(), + ) + if (inputState == InputState.VALID) { + Text( + text = stringResource(R.string.verification_success), + style = TnTTheme.typography.body2Medium, + color = TnTTheme.colors.blueColors.Blue500, + modifier = Modifier.padding( + start = 20.dp, + top = 6.dp, + ), ) - }, - ) + } + } } TnTBottomButton( text = stringResource(core_next), diff --git a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/PTSessionFormPage.kt b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/PTSessionFormPage.kt index 72bd1b57..272db447 100644 --- a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/PTSessionFormPage.kt +++ b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/PTSessionFormPage.kt @@ -2,7 +2,6 @@ package co.kr.tnt.trainee.connect import android.app.DatePickerDialog import androidx.activity.compose.BackHandler -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -10,37 +9,41 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import co.kr.tnt.core.ui.R.string.core_entered_wrong_text import co.kr.tnt.core.ui.R.string.core_next -import co.kr.tnt.designsystem.component.TnTLabeledTextField import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton +import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.TnTTextField2 +import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize import co.kr.tnt.designsystem.theme.TnTTheme +import co.kr.tnt.domain.utils.DateFormatter import co.kr.tnt.feature.trainee.connect.R import co.kr.tnt.ui.component.TnTLoadingScreen import co.kr.tnt.ui.extensions.clearFocusOnTap import co.kr.tnt.ui.utils.throttled import java.time.LocalDate import java.time.ZoneId -import java.time.format.DateTimeFormatter private const val MAX_COUNT = 99 +private val DEFAULT_DATE = LocalDate.of(2000, 1, 1) @Composable internal fun PTSessionFormPage( @@ -57,7 +60,9 @@ internal fun PTSessionFormPage( ) { BackHandler { onClickBack() } - val today = LocalDate.now() + val today = remember { LocalDate.now() } + val dateFormatter = remember { DateFormatter() } + val context = LocalContext.current /** * 모든 입력 값의 유효성을 확인 @@ -109,102 +114,148 @@ internal fun PTSessionFormPage( modifier = Modifier.padding(horizontal = 24.dp), ) Spacer(Modifier.padding(top = 48.dp)) - Row { - Text( - text = stringResource(R.string.pt_start_day), - color = TnTTheme.colors.neutralColors.Neutral900, - style = TnTTheme.typography.body1Bold, - modifier = Modifier.padding(start = 20.dp, bottom = 8.dp), - ) - Text( - text = "*", - color = TnTTheme.colors.redColors.Red500, - style = TnTTheme.typography.body1Bold, - ) - } - DatePicker( - modifier = Modifier.padding(horizontal = 20.dp), - today = today, - selectedDate = sessionStartDate, - onDateSelected = onChangeSessionStartDate, - ) - HorizontalDivider( - thickness = 1.dp, - color = TnTTheme.colors.neutralColors.Neutral200, + TnTSelectableLabeledTextField2( modifier = Modifier.padding(horizontal = 20.dp), + value = sessionStartDate?.let { + dateFormatter.format( + date = it, + pattern = "yyyy/MM/dd", + ) + } ?: "", + placeholder = dateFormatter.format( + date = DEFAULT_DATE, + pattern = "yyyy/MM/dd", + ), + onClickTextField = { + DatePickerDialog( + context, + { _, selectedYear, selectedMonth, selectedDay -> + val newDate = LocalDate.of(selectedYear, selectedMonth + 1, selectedDay) + onChangeSessionStartDate(newDate) + }, + sessionStartDate?.year ?: DEFAULT_DATE.year, + (sessionStartDate?.monthValue?.minus(1)) ?: (DEFAULT_DATE.monthValue - 1), + sessionStartDate?.dayOfMonth ?: DEFAULT_DATE.dayOfMonth, + ) + .apply { + // 오늘 이후는 선택 불가능 + datePicker.maxDate = + today + .atStartOfDay(ZoneId.systemDefault()) + .toInstant() + .toEpochMilli() + } + .show() + }, + title = stringResource(R.string.pt_start_day), + showRequiredTitleBadge = true, + size = TnTTextFieldSize.SMALL, ) Spacer(Modifier.padding(top = 48.dp)) - Row( - horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalAlignment = Alignment.Bottom, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp), - ) { - Column(modifier = Modifier.weight(1f)) { - TnTLabeledTextField( - title = stringResource(R.string.completed_session_until_now), + Column(modifier = Modifier.padding(horizontal = 20.dp)) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f), + ) { + Text( + text = stringResource(R.string.completed_session_until_now), + style = TnTTheme.typography.body1Bold, + color = TnTTheme.colors.neutralColors.Neutral900, + ) + Text( + text = "*", + style = TnTTheme.typography.body1Bold, + color = TnTTheme.colors.redColors.Red500, + ) + } + Spacer(modifier = Modifier.width(32.dp)) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f), + ) { + Text( + text = stringResource(R.string.total_register_session), + style = TnTTheme.typography.body1Bold, + color = TnTTheme.colors.neutralColors.Neutral900, + ) + Text( + text = "*", + style = TnTTheme.typography.body1Bold, + color = TnTTheme.colors.redColors.Red500, + ) + } + } + Spacer(Modifier.height(8.dp)) + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth(), + ) { + TnTTextField2( value = completedSessionCount, placeholder = "0", - isSingleLine = true, - isRequired = true, + isWarning = showCompletedSessionWarning || showTotalSessionWarning, keyboardType = KeyboardType.Number, - showWarning = showCompletedSessionWarning || showTotalSessionWarning, - trailingComponent = { - UnitLabel(R.string.count_unit) + trailing = { + UnitLabel( + modifier = Modifier.align(Alignment.CenterVertically), + stringResId = R.string.count_unit, + ) }, onValueChange = { newValue -> onChangeCompletedSessionCount(newValue) }, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.weight(1f), ) Text( - text = if (showCompletedSessionWarning || showTotalSessionWarning) { - stringResource(core_entered_wrong_text) - } else { - "" - }, - style = TnTTheme.typography.body2Medium, - color = TnTTheme.colors.redColors.Red500, - modifier = Modifier.padding(top = 4.dp), + text = "/", + color = TnTTheme.colors.neutralColors.Neutral600, + style = TnTTheme.typography.body1Medium, ) - } - Text( - text = "/", - color = TnTTheme.colors.neutralColors.Neutral600, - style = TnTTheme.typography.body1Medium, - modifier = Modifier - .padding(8.dp) - .align(Alignment.CenterVertically), - ) - Column(modifier = Modifier.weight(1f)) { - TnTLabeledTextField( - title = stringResource(R.string.total_register_session), + TnTTextField2( value = totalSessionCount, placeholder = "0", - isSingleLine = true, - isRequired = true, + isWarning = showTotalSessionWarning || showCompletedSessionWarning, keyboardType = KeyboardType.Number, - showWarning = showTotalSessionWarning || showCompletedSessionWarning, - trailingComponent = { - UnitLabel(R.string.count_unit) + trailing = { + UnitLabel( + modifier = Modifier.align(Alignment.CenterVertically), + stringResId = R.string.count_unit, + ) }, onValueChange = { newValue -> onChangeTotalSessionCount(newValue) }, - modifier = Modifier.fillMaxWidth(), - ) - Text( - text = if (showTotalSessionWarning || showCompletedSessionWarning) { - stringResource(core_entered_wrong_text) - } else { - "" - }, - style = TnTTheme.typography.body2Medium, - color = TnTTheme.colors.redColors.Red500, - modifier = Modifier.padding(top = 4.dp), + modifier = Modifier.weight(1f), ) } + if (showCompletedSessionWarning || showTotalSessionWarning) { + Spacer(Modifier.height(6.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Box(modifier = Modifier.weight(1f)) { + Text( + text = stringResource(core_entered_wrong_text), + style = TnTTheme.typography.body2Medium, + color = TnTTheme.colors.redColors.Red500, + ) + } + Spacer(modifier = Modifier.width(32.dp)) + Box(modifier = Modifier.weight(1f)) { + Text( + text = stringResource(core_entered_wrong_text), + style = TnTTheme.typography.body2Medium, + color = TnTTheme.colors.redColors.Red500, + ) + } + } + } } } TnTBottomButton( @@ -243,59 +294,12 @@ private fun isCompletedSessionInvalid(completedSession: String, totalSession: St } @Composable -private fun DatePicker( +private fun UnitLabel( modifier: Modifier = Modifier, - today: LocalDate, - selectedDate: LocalDate?, - onDateSelected: (LocalDate) -> Unit, + stringResId: Int, ) { - val context = LocalContext.current - val dateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd") - val date = selectedDate ?: today - - Box( - modifier = modifier - .fillMaxWidth() - .padding(8.dp) - .clickable { - DatePickerDialog( - context, - { _, selectedYear, selectedMonth, selectedDay -> - val newDate = LocalDate.of(selectedYear, selectedMonth + 1, selectedDay) - onDateSelected(newDate) - }, - date.year, - date.monthValue - 1, - date.dayOfMonth, - ) - .apply { - // 오늘 이후는 선택 불가능 - datePicker.maxDate = - today - .atStartOfDay(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli() - } - .show() - }, - ) { - Text( - text = selectedDate?.format(dateFormatter) - ?: stringResource(R.string.birthday_placeholder), - color = if (selectedDate == null) { - TnTTheme.colors.neutralColors.Neutral400 - } else { - TnTTheme.colors.neutralColors.Neutral600 - }, - style = TnTTheme.typography.body1Medium, - textAlign = TextAlign.Start, - ) - } -} - -@Composable -private fun UnitLabel(stringResId: Int) { Text( + modifier = modifier.padding(end = 12.dp), text = stringResource(stringResId), style = TnTTheme.typography.body1Medium, color = TnTTheme.colors.neutralColors.Neutral400, diff --git a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/component/CodeTextField.kt b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/component/CodeTextField.kt deleted file mode 100644 index 344ce60d..00000000 --- a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/component/CodeTextField.kt +++ /dev/null @@ -1,159 +0,0 @@ -package co.kr.tnt.trainee.connect.component - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.foundation.text.BasicTextField -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.HorizontalDivider -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.focus.onFocusChanged -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import co.kr.tnt.designsystem.component.button.TnTTextButton -import co.kr.tnt.designsystem.component.button.model.ButtonSize -import co.kr.tnt.designsystem.component.button.model.ButtonType -import co.kr.tnt.designsystem.theme.TnTTheme -import co.kr.tnt.feature.trainee.connect.R -import co.kr.tnt.trainee.connect.model.InputState -import co.kr.tnt.trainee.connect.model.InputState.FOCUS -import co.kr.tnt.trainee.connect.model.InputState.INVALID -import co.kr.tnt.trainee.connect.model.InputState.UNFOCUSED -import co.kr.tnt.trainee.connect.model.InputState.VALID - -@Composable -fun CodeTextField( - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier = Modifier, - isCodeValid: InputState?, - trailingComponent: @Composable BoxScope.() -> Unit = {}, -) { - var codeState by remember { mutableStateOf(UNFOCUSED) } - - val color = when { - isCodeValid == VALID -> TnTTheme.colors.blueColors.Blue500 - isCodeValid == INVALID -> TnTTheme.colors.redColors.Red500 - codeState == FOCUS -> TnTTheme.colors.neutralColors.Neutral600 - else -> TnTTheme.colors.neutralColors.Neutral200 - } - - Column(modifier = modifier.fillMaxWidth()) { - Row { - Text( - text = stringResource(R.string.my_invite_code), - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.neutralColors.Neutral900, - ) - Text( - text = "*", - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.redColors.Red500, - ) - } - Spacer(Modifier.padding(top = 8.dp)) - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - BasicTextField( - value = value, - onValueChange = { onValueChange(it) }, - singleLine = true, - cursorBrush = SolidColor(TnTTheme.colors.neutralColors.Neutral900), - textStyle = TnTTheme.typography.body1Medium.copy( - color = TnTTheme.colors.neutralColors.Neutral600, - ), - modifier = Modifier - .weight(1f) - .onFocusChanged { - if (it.isFocused) { - codeState = FOCUS - } else if (value.isEmpty()) { - codeState = UNFOCUSED - } - } - .padding(horizontal = 8.dp), - decorationBox = { innerTextField -> - if (value.isEmpty()) { - Text( - text = stringResource(R.string.enter_the_code), - style = TnTTheme.typography.body1Medium, - color = TnTTheme.colors.neutralColors.Neutral400, - ) - } - innerTextField() - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Text, - ), - ) - Box( - modifier = Modifier - .wrapContentSize(Alignment.Center) - .align(Alignment.CenterVertically), - content = trailingComponent, - ) - } - HorizontalDivider( - thickness = 1.dp, - color = color, - ) - if (isCodeValid == VALID) { - Text( - text = stringResource(R.string.verification_success), - style = TnTTheme.typography.body2Medium, - color = color, - modifier = Modifier.padding(top = 6.dp), - ) - } - if (isCodeValid == INVALID) { - Text( - text = stringResource(R.string.verification_fail), - style = TnTTheme.typography.body2Medium, - color = color, - modifier = Modifier.padding(top = 6.dp), - ) - } - } -} - -@Preview(showBackground = true) -@Composable -private fun CodeTextFieldPreview() { - TnTTheme { - var text by remember { mutableStateOf("") } - val isValid = if (text.length == 8) VALID else INVALID - - CodeTextField( - value = text, - onValueChange = { - text = it - }, - isCodeValid = isValid, - trailingComponent = { - TnTTextButton( - text = "인증하기", - onClick = {}, - type = ButtonType.Primary, - size = ButtonSize.Small, - ) - }, - ) - } -} diff --git a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordContract.kt b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordContract.kt index 68bcc477..b49e3d19 100644 --- a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordContract.kt +++ b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordContract.kt @@ -18,7 +18,6 @@ internal class TraineeMealRecordContract { val mealType: String = "", val memo: String = "", val isDateFieldFocused: Boolean = false, - val isTimeFieldFocused: Boolean = false, val isMealRecordValid: Boolean = false, val showWarning: Boolean = false, val dialogState: DialogState = DialogState.NONE, @@ -48,7 +47,6 @@ internal class TraineeMealRecordContract { data object OnClickDeleteImage : TraineeMealRecordUiEvent data object OnClickMealDate : TraineeMealRecordUiEvent data class OnSelectMealDate(val date: LocalDate) : TraineeMealRecordUiEvent - data object OnClickMealTime : TraineeMealRecordUiEvent data class OnSelectMealTime(val time: LocalTime) : TraineeMealRecordUiEvent data object OnClickCloseBottomSheet : TraineeMealRecordUiEvent data class OnSelectMealType(val mealType: String) : TraineeMealRecordUiEvent diff --git a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordScreen.kt b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordScreen.kt index bd47e785..775fc6c3 100644 --- a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordScreen.kt +++ b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordScreen.kt @@ -59,7 +59,6 @@ import co.kr.tnt.designsystem.component.TnTBottomSheetDialog import co.kr.tnt.designsystem.component.TnTDivider import co.kr.tnt.designsystem.component.TnTIconPopupDialog import co.kr.tnt.designsystem.component.TnTOutlinedTextField -import co.kr.tnt.designsystem.component.TnTSelectableTextField import co.kr.tnt.designsystem.component.TnTSingleButtonPopupDialog import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.TnTWheelTimePicker @@ -70,6 +69,7 @@ import co.kr.tnt.designsystem.component.calendar.TnTCalendarSelector import co.kr.tnt.designsystem.component.calendar.TnTMonthCalendar import co.kr.tnt.designsystem.component.calendar.model.DayState import co.kr.tnt.designsystem.component.calendar.utils.rememberMostVisibleMonth +import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextField2 import co.kr.tnt.designsystem.snackbar.LocalSnackbar import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.domain.UserProfilePolicy @@ -132,7 +132,6 @@ internal fun TraineeMealRecordRoute( showBottomSheet = true }, onClickTimeSection = { - viewModel.setEvent(TraineeMealRecordUiEvent.OnClickMealTime) showBottomSheet = true }, onSelectMealType = { type -> @@ -286,13 +285,11 @@ private fun TraineeMealRecordScreen( ) { MealDate( date = state.date, - focusState = state.isDateFieldFocused, dateFormatter = dateFormatter, onClick = onClickDateSection, ) MealTime( time = state.time, - focusState = state.isTimeFieldFocused, dateFormatter = dateFormatter, onClick = onClickTimeSection, ) @@ -304,7 +301,7 @@ private fun TraineeMealRecordScreen( state = state, onValueChange = onChangeMemo, ) - Spacer(Modifier.height(64.dp)) + Spacer(Modifier.height(36.dp)) } } } @@ -414,36 +411,30 @@ private fun MealImageSelector( @Composable private fun MealDate( date: LocalDate, - focusState: Boolean, dateFormatter: DateFormatter, onClick: () -> Unit, ) { - TnTSelectableTextField( + TnTSelectableLabeledTextField2( title = stringResource(R.string.meal_date), value = dateFormatter.format(date, "yyyy/MM/dd"), - onValueChange = { }, - isRequired = true, - shouldClearFocus = focusState.not(), - onClick = onClick, + showRequiredTitleBadge = true, + onClickTextField = onClick, ) } @Composable private fun MealTime( time: LocalTime?, - focusState: Boolean, dateFormatter: DateFormatter, onClick: () -> Unit, ) { val now = LocalTime.now() - TnTSelectableTextField( + TnTSelectableLabeledTextField2( title = stringResource(R.string.meal_time), value = time?.let { dateFormatter.format(it, "HH:mm") } ?: "", - onValueChange = { }, - isRequired = true, + showRequiredTitleBadge = true, placeholder = dateFormatter.format(now, "HH:mm"), - shouldClearFocus = focusState.not(), - onClick = onClick, + onClickTextField = onClick, ) } diff --git a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordViewModel.kt b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordViewModel.kt index b99c9acc..0f5022c5 100644 --- a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordViewModel.kt +++ b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordViewModel.kt @@ -36,11 +36,9 @@ internal class TraineeMealRecordViewModel @Inject constructor( ).validateMealRecord() } - is TraineeMealRecordUiEvent.OnClickMealTime -> updateState { copy(isTimeFieldFocused = true) } is TraineeMealRecordUiEvent.OnSelectMealTime -> updateState { copy( time = event.time, - isTimeFieldFocused = false, ).validateMealRecord() } @@ -65,7 +63,6 @@ internal class TraineeMealRecordViewModel @Inject constructor( updateState { copy( isDateFieldFocused = false, - isTimeFieldFocused = false, ) } } diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt index fef4399e..ee88e7f2 100644 --- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt +++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt @@ -1,9 +1,7 @@ package co.kr.tnt.trainee.signup import android.app.DatePickerDialog -import android.content.Context import androidx.activity.compose.BackHandler -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -16,7 +14,6 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -25,7 +22,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import co.kr.tnt.core.ui.R.string.core_entered_wrong_text @@ -34,9 +30,11 @@ import co.kr.tnt.core.ui.R.string.core_height_unit import co.kr.tnt.core.ui.R.string.core_next import co.kr.tnt.core.ui.R.string.core_weight_label import co.kr.tnt.core.ui.R.string.core_weight_unit -import co.kr.tnt.designsystem.component.TnTLabeledTextField import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.feature.trainee.signup.R import co.kr.tnt.trainee.signup.TraineeSignUpContract.TraineeSignUpUiState @@ -80,23 +78,33 @@ internal fun TraineeBasicInfoPage( subTitle = stringResource(R.string.basic_info_for_trainer), ) Spacer(Modifier.padding(top = 48.dp)) - Text( - text = stringResource(R.string.birthday_label), - color = TnTTheme.colors.neutralColors.Neutral900, - style = TnTTheme.typography.body1Bold, - modifier = Modifier.padding(start = 20.dp, bottom = 8.dp), - ) - BirthdayPicker( - modifier = Modifier.padding(horizontal = 20.dp), - context = context, - today = today, - selectedDate = state.birthday, - onDateSelected = onChangeBirthday, - ) - HorizontalDivider( - thickness = 1.dp, - color = TnTTheme.colors.neutralColors.Neutral200, + TnTSelectableLabeledTextField2( modifier = Modifier.padding(horizontal = 20.dp), + value = state.birthday?.format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + ?: stringResource(R.string.birthday_placeholder), + onClickTextField = { + DatePickerDialog( + context, + { _, selectedYear, selectedMonth, selectedDay -> + val newDate = LocalDate.of(selectedYear, selectedMonth + 1, selectedDay) + onChangeBirthday(newDate) + }, + state.birthday?.year ?: today.year, + (state.birthday?.monthValue?.minus(1)) ?: (today.monthValue - 1), + state.birthday?.dayOfMonth ?: today.dayOfMonth, + ) + .apply { + // 오늘 이후는 선택 불가능 + datePicker.maxDate = + today + .atStartOfDay(ZoneId.systemDefault()) + .toInstant() + .toEpochMilli() + } + .show() + }, + title = stringResource(R.string.birthday_label), + size = TnTTextFieldSize.SMALL, ) Spacer(Modifier.padding(top = 48.dp)) Row( @@ -105,30 +113,34 @@ internal fun TraineeBasicInfoPage( .fillMaxWidth() .padding(horizontal = 20.dp), ) { - TnTLabeledTextField( + TnTLabeledTextField2( title = stringResource(core_height_label), value = state.height ?: "", placeholder = "0", - isSingleLine = true, - showWarning = state.isHeightValid.not(), + isWarning = state.isHeightValid.not(), warningMessage = stringResource(core_entered_wrong_text), keyboardType = KeyboardType.Number, - trailingComponent = { - UnitLabel(core_height_unit) + trailing = { + UnitLabel( + modifier = Modifier.align(Alignment.CenterVertically), + stringResId = core_height_unit, + ) }, onValueChange = onChangeHeight, modifier = Modifier.weight(1f), ) - TnTLabeledTextField( + TnTLabeledTextField2( title = stringResource(core_weight_label), value = state.weight ?: "", placeholder = "00.0", - isSingleLine = true, - showWarning = state.isWeightValid.not(), + isWarning = state.isWeightValid.not(), warningMessage = stringResource(core_entered_wrong_text), keyboardType = KeyboardType.Number, - trailingComponent = { - UnitLabel(core_weight_unit) + trailing = { + UnitLabel( + modifier = Modifier.align(Alignment.CenterVertically), + stringResId = core_weight_unit, + ) }, onValueChange = onChangeWeight, modifier = Modifier.weight(1f), @@ -146,60 +158,12 @@ internal fun TraineeBasicInfoPage( } @Composable -private fun BirthdayPicker( +private fun UnitLabel( modifier: Modifier = Modifier, - context: Context, - today: LocalDate, - selectedDate: LocalDate?, - onDateSelected: (LocalDate) -> Unit, + stringResId: Int, ) { - val dateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd") - val date = selectedDate ?: LocalDate.of(2001, 1, 1) - - Box( - modifier = modifier - .fillMaxWidth() - .padding(8.dp) - .clickable { - DatePickerDialog( - context, - { _, selectedYear, selectedMonth, selectedDay -> - val newDate = LocalDate.of(selectedYear, selectedMonth + 1, selectedDay) - onDateSelected(newDate) - }, - date.year, - date.monthValue - 1, - date.dayOfMonth, - ) - .apply { - // 오늘 이후는 선택 불가능 - val todayMillis = today - .atStartOfDay(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli() - - datePicker.maxDate = todayMillis - 1 - } - .show() - }, - ) { - Text( - text = selectedDate?.format(dateFormatter) - ?: stringResource(R.string.birthday_placeholder), - color = if (selectedDate == null) { - TnTTheme.colors.neutralColors.Neutral400 - } else { - TnTTheme.colors.neutralColors.Neutral600 - }, - style = TnTTheme.typography.body1Medium, - textAlign = TextAlign.Start, - ) - } -} - -@Composable -private fun UnitLabel(stringResId: Int) { Text( + modifier = modifier.padding(end = 12.dp), text = stringResource(stringResId), style = TnTTheme.typography.body1Medium, color = TnTTheme.colors.neutralColors.Neutral400, diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt index f13933ce..f7c7d26c 100644 --- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt +++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt @@ -27,10 +27,10 @@ import androidx.compose.ui.unit.dp import co.kr.tnt.core.ui.R.string.core_name import co.kr.tnt.core.ui.R.string.core_next import co.kr.tnt.core.ui.R.string.core_text_length_and_format_warning -import co.kr.tnt.designsystem.component.TnTLabeledTextFieldWithCounter import co.kr.tnt.designsystem.component.TnTProfileImage import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.domain.UserProfilePolicy import co.kr.tnt.feature.trainee.signup.R @@ -99,7 +99,7 @@ internal fun TraineeProfileSetupPage( }, ) Spacer(Modifier.padding(top = 60.dp)) - TnTLabeledTextFieldWithCounter( + TnTLabeledTextField2( title = stringResource(core_name), value = state.name, onValueChange = { newValue -> @@ -108,9 +108,8 @@ internal fun TraineeProfileSetupPage( modifier = Modifier.padding(horizontal = 20.dp), placeholder = stringResource(R.string.enter_your_name), maxLength = UserProfilePolicy.USER_NAME_MAX_LENGTH, - isSingleLine = true, - showWarning = !state.isNameValid, - isRequired = true, + isWarning = !state.isNameValid, + showRequiredTitleBadge = true, warningMessage = stringResource( core_text_length_and_format_warning, UserProfilePolicy.USER_NAME_MAX_LENGTH, diff --git a/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt b/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt index c9aeadd5..cb289277 100644 --- a/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt +++ b/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt @@ -37,7 +37,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle @@ -59,9 +58,7 @@ import co.kr.tnt.core.ui.R.string.core_ok import co.kr.tnt.designsystem.component.TnTBottomSheetDialog import co.kr.tnt.designsystem.component.TnTDivider import co.kr.tnt.designsystem.component.TnTIconPopupDialog -import co.kr.tnt.designsystem.component.TnTOutlinedTextField import co.kr.tnt.designsystem.component.TnTSingleButtonPopupDialog -import co.kr.tnt.designsystem.component.TnTTextField import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.TnTWheelTimePicker import co.kr.tnt.designsystem.component.button.TnTBottomButton @@ -72,6 +69,9 @@ import co.kr.tnt.designsystem.component.calendar.TnTCalendarSelector import co.kr.tnt.designsystem.component.calendar.TnTMonthCalendar import co.kr.tnt.designsystem.component.calendar.model.DayState import co.kr.tnt.designsystem.component.calendar.utils.rememberMostVisibleMonth +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize import co.kr.tnt.designsystem.snackbar.LocalSnackbar import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.domain.model.MemberInfo @@ -324,44 +324,25 @@ private fun Selector( modifier: Modifier = Modifier, isWarning: Boolean = false, ) { - val focusManager = LocalFocusManager.current - - Column(modifier = modifier) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = title, - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.neutralColors.Neutral900, - ) - Text( - text = "*", - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.redColors.Red500, + TnTSelectableLabeledTextField2( + title = title, + showRequiredTitleBadge = true, + value = value, + isWarning = isWarning, + placeholder = placeholder, + modifier = modifier, + trailing = { + Icon( + modifier = Modifier + .align(Alignment.CenterVertically) + .padding(end = 8.dp), + painter = painterResource(ic_arrow_down), + contentDescription = null, + tint = TnTTheme.colors.neutralColors.Neutral400, ) - } - Spacer(modifier = Modifier.height(8.dp)) - TnTTextField( - value = value, - enabled = false, - isSingleLine = true, - showWarning = isWarning, - placeholder = placeholder, - modifier = Modifier - .fillMaxWidth() - .clickable { - focusManager.clearFocus() - onClick() - }, - trailingComponent = { - Icon( - painter = painterResource(ic_arrow_down), - contentDescription = null, - tint = TnTTheme.colors.neutralColors.Neutral400, - ) - }, - onValueChange = { }, - ) - } + }, + onClickTextField = onClick, + ) } @Composable @@ -500,19 +481,15 @@ private fun Memo( isWarning: Boolean, onValueChanged: (String) -> Unit, ) { - Text( - text = stringResource(R.string.write_memo), - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.neutralColors.Neutral900, - ) - Spacer(modifier = Modifier.height(8.dp)) - TnTOutlinedTextField( + TnTLabeledTextField2( + title = stringResource(R.string.write_memo), value = value, onValueChange = onValueChanged, + modifier = Modifier.fillMaxWidth(), + size = TnTTextFieldSize.LARGE, maxLength = 30, - isError = isWarning, + isWarning = isWarning, warningMessage = stringResource(core_length_warning, 30), - modifier = Modifier.fillMaxWidth(), ) } diff --git a/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/TrainerInviteScreen.kt b/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/TrainerInviteScreen.kt index eafb4be9..e9b4df90 100644 --- a/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/TrainerInviteScreen.kt +++ b/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/TrainerInviteScreen.kt @@ -4,13 +4,9 @@ import androidx.activity.compose.BackHandler import androidx.compose.foundation.clickable 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.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold @@ -18,7 +14,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext @@ -33,9 +28,9 @@ import co.kr.tnt.core.ui.R.string.core_connect import co.kr.tnt.core.ui.R.string.core_skip import co.kr.tnt.designsystem.component.TnTTopBar import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton -import co.kr.tnt.designsystem.component.button.TnTTextButton import co.kr.tnt.designsystem.component.button.model.ButtonSize import co.kr.tnt.designsystem.component.button.model.ButtonType +import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextFieldWithTextButton import co.kr.tnt.designsystem.snackbar.LocalSnackbar import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.feature.trainer.invite.R @@ -151,54 +146,17 @@ internal fun TrainerInviteScreen( modifier = Modifier.padding(horizontal = 24.dp), ) Spacer(Modifier.padding(top = 48.dp)) - Column(Modifier.padding(horizontal = 20.dp)) { - Row { - Text( - text = stringResource(R.string.my_invite_code), - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.neutralColors.Neutral900, - ) - Text( - text = "*", - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.redColors.Red500, - ) - } - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Column(modifier = Modifier.weight(1f)) { - Text( - text = state.inviteCode, - style = TnTTheme.typography.body1Medium, - color = TnTTheme.colors.neutralColors.Neutral600, - modifier = Modifier - .padding(8.dp) - .clickable { onClickCode(state.inviteCode) }, - ) - HorizontalDivider( - thickness = 1.dp, - color = TnTTheme.colors.neutralColors.Neutral300, - modifier = Modifier.padding(top = 4.dp), - ) - } - Box( - modifier = Modifier - .wrapContentSize(Alignment.CenterEnd) - .align(Alignment.CenterVertically) - .padding(vertical = 4.dp), - content = { - TnTTextButton( - text = stringResource(R.string.code_reissue), - size = ButtonSize.Small, - type = ButtonType.Gray, - onClick = { onClickRegenerate() }, - ) - }, - ) - } - } + TnTSelectableLabeledTextFieldWithTextButton( + modifier = Modifier.padding(horizontal = 20.dp), + title = stringResource(R.string.my_invite_code), + showRequiredTitleBadge = true, + value = state.inviteCode, + trailingButtonTitle = stringResource(R.string.code_reissue), + trailingButtonSize = ButtonSize.Small, + trailingButtonType = ButtonType.Gray, + onClickTextField = { onClickCode(state.inviteCode) }, + onClickTrailingButton = { onClickRegenerate() }, + ) } } } diff --git a/feature/trainer/modifymyinfo/src/main/java/co/kr/tnt/trainer/modifymyinfo/TrainerModifyMyInfo.kt b/feature/trainer/modifymyinfo/src/main/java/co/kr/tnt/trainer/modifymyinfo/TrainerModifyMyInfo.kt index 92157a2d..e5d0dfa4 100644 --- a/feature/trainer/modifymyinfo/src/main/java/co/kr/tnt/trainer/modifymyinfo/TrainerModifyMyInfo.kt +++ b/feature/trainer/modifymyinfo/src/main/java/co/kr/tnt/trainer/modifymyinfo/TrainerModifyMyInfo.kt @@ -30,10 +30,10 @@ import co.kr.tnt.core.ui.R.string.core_ok import co.kr.tnt.core.ui.R.string.core_text_length_and_format_warning import co.kr.tnt.core.ui.R.string.core_unsaved_changes_warning import co.kr.tnt.designsystem.component.TnTIconPopupDialog -import co.kr.tnt.designsystem.component.TnTLabeledTextFieldWithCounter import co.kr.tnt.designsystem.component.TnTProfileImage import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 import co.kr.tnt.designsystem.snackbar.LocalSnackbar import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.domain.UserProfilePolicy @@ -142,7 +142,7 @@ private fun TrainerModifyMyInfoScreen( }, ) Spacer(modifier = Modifier.height(48.dp)) - TnTLabeledTextFieldWithCounter( + TnTLabeledTextField2( title = stringResource(core_name), value = state.name, onValueChange = { newValue -> @@ -151,13 +151,13 @@ private fun TrainerModifyMyInfoScreen( modifier = Modifier.padding(horizontal = 20.dp), placeholder = stringResource(core_name_placeholder), maxLength = UserProfilePolicy.USER_NAME_MAX_LENGTH, - isSingleLine = true, - showWarning = state.isValidName.not(), - isRequired = true, + isWarning = state.isValidName.not(), + showRequiredTitleBadge = true, warningMessage = stringResource( core_text_length_and_format_warning, UserProfilePolicy.USER_NAME_MAX_LENGTH, ), + requestFocusOnStart = true, ) Spacer(modifier = Modifier.weight(1f)) TnTBottomButton( diff --git a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt index 2a9cf106..dc2474a6 100644 --- a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt +++ b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt @@ -29,10 +29,10 @@ import co.kr.tnt.core.ui.R.string.core_name import co.kr.tnt.core.ui.R.string.core_name_placeholder import co.kr.tnt.core.ui.R.string.core_next import co.kr.tnt.core.ui.R.string.core_text_length_and_format_warning -import co.kr.tnt.designsystem.component.TnTLabeledTextFieldWithCounter import co.kr.tnt.designsystem.component.TnTProfileImage import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.domain.UserProfilePolicy import co.kr.tnt.feature.trainer.signup.R @@ -102,7 +102,7 @@ internal fun TrainerProfileSetupPage( }, ) Spacer(Modifier.padding(top = 60.dp)) - TnTLabeledTextFieldWithCounter( + TnTLabeledTextField2( title = stringResource(core_name), value = state.name, onValueChange = { newValue -> @@ -111,9 +111,8 @@ internal fun TrainerProfileSetupPage( modifier = Modifier.padding(horizontal = 20.dp), placeholder = stringResource(core_name_placeholder), maxLength = UserProfilePolicy.USER_NAME_MAX_LENGTH, - isSingleLine = true, - showWarning = state.isNameValid.not(), - isRequired = true, + isWarning = state.isNameValid.not(), + showRequiredTitleBadge = true, warningMessage = stringResource( core_text_length_and_format_warning, UserProfilePolicy.USER_NAME_MAX_LENGTH, From 6aea0445802455b61c59800dfae2a37e3e6f8d13 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:22:27 +0900 Subject: [PATCH 05/14] =?UTF-8?q?[TNT-000]=20fix:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=8B=88=20=EC=8B=9D=EB=8B=A8=20=EA=B8=B0=EB=A1=9D=20?= =?UTF-8?q?=EC=8B=9C=20=EA=B8=B0=EB=A1=9D=20=EC=A2=85=EB=A3=8C=20=EC=95=88?= =?UTF-8?q?=EB=82=B4=20=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=98=20[=ED=99=95=EC=9D=B8]=20=EC=9D=84=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=ED=95=98=EC=97=AC=EB=8F=84=20=EC=A2=85=EB=A3=8C?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8D=98=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/tnt/trainee/mealrecord/TraineeMealRecordViewModel.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordViewModel.kt b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordViewModel.kt index 0f5022c5..07fbd712 100644 --- a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordViewModel.kt +++ b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordViewModel.kt @@ -55,7 +55,10 @@ internal class TraineeMealRecordViewModel @Inject constructor( sendEffect(TraineeMealRecordSideEffect.NavigateToHome) } - TraineeMealRecordUiEvent.OnDismissDialog -> updateState { copy(dialogState = DialogState.NONE) } + TraineeMealRecordUiEvent.OnDismissDialog -> { + updateState { copy(dialogState = DialogState.NONE) } + sendEffect(TraineeMealRecordSideEffect.NavigateToHome) + } } } From e1af2dadc37a024d3b797fe5d49ffc7ef696ffbe Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:22:58 +0900 Subject: [PATCH 06/14] =?UTF-8?q?[TNT-000]=20fix:=20SignUpRequest=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(goalContents=20->=20ptGoals)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/co/kr/data/network/model/SignUpRequest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/network/src/main/java/co/kr/data/network/model/SignUpRequest.kt b/data/network/src/main/java/co/kr/data/network/model/SignUpRequest.kt index cf38ac49..39387e48 100644 --- a/data/network/src/main/java/co/kr/data/network/model/SignUpRequest.kt +++ b/data/network/src/main/java/co/kr/data/network/model/SignUpRequest.kt @@ -17,7 +17,7 @@ data class SignUpRequest( val birthday: String? = null, val height: Double? = null, val weight: Double? = null, - val goalContents: List? = null, + val ptGoals: List? = null, val cautionNote: String? = "", ) @@ -34,7 +34,7 @@ fun User.toSignUpRequest( birthday = null, height = null, weight = null, - goalContents = null, + ptGoals = null, cautionNote = null, socialType = socialType, socialId = socialId, @@ -51,7 +51,7 @@ fun User.toSignUpRequest( birthday = birthday?.toString(), height = height?.toDouble(), weight = weight, - goalContents = ptPurpose, + ptGoals = ptPurpose, cautionNote = caution?.ifBlank { null }, socialType = socialType, socialId = socialId, From 14d923c7f232f350827dc6835ba335b491d0a4e1 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:23:42 +0900 Subject: [PATCH 07/14] =?UTF-8?q?[TNT-309]=20fix:=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=ED=8F=B0=ED=8A=B8=20?= =?UTF-8?q?=EB=82=B4=20=EC=BB=A4=EC=8A=A4=ED=85=80=20LineHeightStyle=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/co/kr/tnt/designsystem/theme/Type.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/theme/Type.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/theme/Type.kt index 7281e170..acab36cd 100644 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/theme/Type.kt +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/theme/Type.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.LineHeightStyle import androidx.compose.ui.unit.sp import co.kr.tnt.core.designsystem.R @@ -18,6 +19,10 @@ val Pretendard = FontFamily( private val PretendardStyle = TextStyle( fontFamily = Pretendard, + lineHeightStyle = LineHeightStyle( + alignment = LineHeightStyle.Alignment.Center, + trim = LineHeightStyle.Trim.None, + ), ) // Set of Material typography styles to start with From 28c8130946b2a08c0a8bd9ecb215bc35105f8ba9 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:32:52 +0900 Subject: [PATCH 08/14] =?UTF-8?q?[TNT-000]=20fix:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=8B=88=20=EC=97=B0=EA=B2=B0=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=92=A4=EB=A1=9C=20=EA=B0=80=EC=A7=80?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8D=98=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/co/kr/tnt/trainee/connect/TraineeConnectScreen.kt | 2 +- .../java/co/kr/tnt/trainee/connect/TraineeConnectViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectScreen.kt b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectScreen.kt index 13d2f520..fd69a6bf 100644 --- a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectScreen.kt +++ b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectScreen.kt @@ -28,7 +28,7 @@ internal fun TraineeConnectRoute( TraineeConnectScreen( state = state, screenMode = screenMode, - onClickBack = { viewModel.setEvent(TraineeConnectUiEvent.OnChangeDialogState) }, + onClickBack = { viewModel.setEvent(TraineeConnectUiEvent.OnClickBack) }, onClickNext = { viewModel.setEvent(TraineeConnectUiEvent.OnClickNext) }, onClickSkip = { viewModel.setEvent(TraineeConnectUiEvent.OnClickSkip) }, onChangeInviteCode = { code -> diff --git a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectViewModel.kt b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectViewModel.kt index d09deac5..73cec5ae 100644 --- a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectViewModel.kt +++ b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectViewModel.kt @@ -85,7 +85,7 @@ internal class TraineeConnectViewModel @Inject constructor( runCatching { currentState.run { connectRepository.connectRequest( - invitationCode = inviteCode, + invitationCode = inviteCode.trim(), startDate = (sessionStartDate ?: LocalDate.now()).toString(), completedSession = completedSessionCount.toInt(), totalSession = totalSessionCount.toInt(), From 5e3c09cef147edf0d6f5bab301588d4736ad0123 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:37:14 +0900 Subject: [PATCH 09/14] =?UTF-8?q?[TNT-000]=20fix:=20=EB=B0=B1=EC=97=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD=20(ptGoal=20->=20ptGoals)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/co/kr/data/network/model/ConnectedTraineeResponse.kt | 4 ++-- .../java/co/kr/data/network/model/UpdateUserInfoRequest.kt | 2 +- .../src/main/java/co/kr/tnt/domain/model/ConnectedResult.kt | 2 +- .../java/co/kr/tnt/trainer/connect/TrainerConnectViewModel.kt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/network/src/main/java/co/kr/data/network/model/ConnectedTraineeResponse.kt b/data/network/src/main/java/co/kr/data/network/model/ConnectedTraineeResponse.kt index 2affcfae..d84cd459 100644 --- a/data/network/src/main/java/co/kr/data/network/model/ConnectedTraineeResponse.kt +++ b/data/network/src/main/java/co/kr/data/network/model/ConnectedTraineeResponse.kt @@ -16,7 +16,7 @@ data class Trainee( val traineeProfileImageUrl: String, val cautionNote: String?, val height: Double?, - val ptGoal: String, + val ptGoals: List, val traineeAge: Int?, val weight: Double?, ) @@ -38,6 +38,6 @@ fun ConnectedTraineeResponse.toDomain(): ConnectedResult = age = trainee.traineeAge, height = trainee.height, weight = trainee.weight, - ptGoal = trainee.ptGoal, + ptGoals = trainee.ptGoals, cautionNote = trainee.cautionNote, ) diff --git a/data/network/src/main/java/co/kr/data/network/model/UpdateUserInfoRequest.kt b/data/network/src/main/java/co/kr/data/network/model/UpdateUserInfoRequest.kt index facd2327..130dcdc5 100644 --- a/data/network/src/main/java/co/kr/data/network/model/UpdateUserInfoRequest.kt +++ b/data/network/src/main/java/co/kr/data/network/model/UpdateUserInfoRequest.kt @@ -12,5 +12,5 @@ data class UpdateUserInfoRequest( val height: Double? = null, val weight: Double? = null, val cautionNote: String? = null, - val goalContents: List? = null, + val ptGoals: List? = null, ) diff --git a/domain/src/main/java/co/kr/tnt/domain/model/ConnectedResult.kt b/domain/src/main/java/co/kr/tnt/domain/model/ConnectedResult.kt index a2e18e46..28adbf42 100644 --- a/domain/src/main/java/co/kr/tnt/domain/model/ConnectedResult.kt +++ b/domain/src/main/java/co/kr/tnt/domain/model/ConnectedResult.kt @@ -8,6 +8,6 @@ data class ConnectedResult( val age: Int?, val height: Double?, val weight: Double?, - val ptGoal: String, + val ptGoals: List, val cautionNote: String?, ) diff --git a/feature/trainer/connect/src/main/java/co/kr/tnt/trainer/connect/TrainerConnectViewModel.kt b/feature/trainer/connect/src/main/java/co/kr/tnt/trainer/connect/TrainerConnectViewModel.kt index 13ffb596..b2580e89 100644 --- a/feature/trainer/connect/src/main/java/co/kr/tnt/trainer/connect/TrainerConnectViewModel.kt +++ b/feature/trainer/connect/src/main/java/co/kr/tnt/trainer/connect/TrainerConnectViewModel.kt @@ -57,7 +57,7 @@ internal class TrainerConnectViewModel @Inject constructor( age = result.age, weight = result.weight, height = result.height?.toInt(), - ptPurpose = listOf(result.ptGoal), + ptPurpose = result.ptGoals, caution = result.cautionNote, isConnected = true, ), From 37415e369461201dcbf384f120b8ca553b651a13 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Tue, 18 Nov 2025 23:05:37 +0900 Subject: [PATCH 10/14] =?UTF-8?q?[TNT-309]=20refactor:=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20TextField=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20"2"=20?= =?UTF-8?q?=EC=A0=91=EB=AF=B8=EC=82=AC=20=EC=A0=9C=EA=B1=B0=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tnt/designsystem/component/TextField.kt | 628 ------------------ .../component/textfield/LabeledTextField.kt | 32 +- ...eTextField_2.kt => SelectableTextField.kt} | 20 +- .../{TextField_2.kt => TextField.kt} | 6 +- .../tnt/trainee/connect/PTSessionFormPage.kt | 10 +- .../mealrecord/TraineeMealRecordScreen.kt | 6 +- .../trainee/signup/TraineeBasicInfoPage.kt | 10 +- .../trainee/signup/TraineeProfileSetupPage.kt | 4 +- .../addptsession/AddPtSessionScreen.kt | 8 +- .../modifymyinfo/TrainerModifyMyInfo.kt | 4 +- .../trainer/signup/TrainerProfileSetupPage.kt | 4 +- 11 files changed, 52 insertions(+), 680 deletions(-) delete mode 100644 core/designsystem/src/main/java/co/kr/tnt/designsystem/component/TextField.kt rename core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/{SelectableTextField_2.kt => SelectableTextField.kt} (94%) rename core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/{TextField_2.kt => TextField.kt} (99%) diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/TextField.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/TextField.kt deleted file mode 100644 index 8750219c..00000000 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/TextField.kt +++ /dev/null @@ -1,628 +0,0 @@ -package co.kr.tnt.designsystem.component - -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentSize -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.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -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.focus.onFocusChanged -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import co.kr.tnt.core.designsystem.R -import co.kr.tnt.designsystem.component.button.TnTTextButton -import co.kr.tnt.designsystem.component.button.model.ButtonSize -import co.kr.tnt.designsystem.theme.TnTTheme -import java.time.LocalDate - -@Composable -fun TnTTextField( - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier = Modifier, - placeholder: String? = stringResource(R.string.placeholder_content_input), - enabled: Boolean = true, - isSingleLine: Boolean = false, - showWarning: Boolean = false, - warningMessage: String? = null, - keyboardType: KeyboardType = KeyboardType.Text, - trailingComponent: @Composable BoxScope.() -> Unit = {}, -) { - val keyboardController = LocalSoftwareKeyboardController.current - var isFocused by remember { mutableStateOf(false) } - - val lineColor = when { - showWarning -> TnTTheme.colors.redColors.Red500 - isFocused -> TnTTheme.colors.neutralColors.Neutral600 - else -> TnTTheme.colors.neutralColors.Neutral200 - } - - Column( - modifier = modifier.fillMaxWidth(), - ) { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - BasicTextField( - value = value, - onValueChange = onValueChange, - enabled = enabled, - singleLine = isSingleLine, - cursorBrush = SolidColor(TnTTheme.colors.neutralColors.Neutral900), - textStyle = TnTTheme.typography.body1Medium.copy( - color = TnTTheme.colors.neutralColors.Neutral600, - ), - modifier = Modifier - .weight(1f) - .onFocusChanged { focusState -> - isFocused = focusState.isFocused - } - .padding(8.dp), - decorationBox = { innerTextField -> - if (value.isEmpty() && placeholder != null) { - Text( - text = placeholder, - style = TnTTheme.typography.body1Medium, - color = TnTTheme.colors.neutralColors.Neutral400, - ) - } - innerTextField() - }, - keyboardOptions = KeyboardOptions( - keyboardType = keyboardType, - ), - keyboardActions = KeyboardActions( - onDone = { - keyboardController?.hide() - isFocused = false - }, - ), - ) - - Box( - modifier = Modifier - .wrapContentSize(Alignment.Center) - .align(Alignment.CenterVertically) - .padding(vertical = 4.dp), - content = trailingComponent, - ) - } - - HorizontalDivider( - thickness = 1.dp, - color = lineColor, - ) - - if (showWarning && !warningMessage.isNullOrEmpty()) { - Text( - text = warningMessage, - style = TnTTheme.typography.body2Medium, - color = TnTTheme.colors.redColors.Red500, - modifier = Modifier.padding(top = 6.dp), - ) - } - } -} - -@Composable -fun TnTLabeledTextFieldWithCounter( - title: String, - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier = Modifier, - placeholder: String? = stringResource(R.string.placeholder_content_input), - maxLength: Int = 15, - isSingleLine: Boolean = false, - showWarning: Boolean = false, - isRequired: Boolean = false, - warningMessage: String? = null, - keyboardType: KeyboardType = KeyboardType.Text, - trailingComponent: @Composable BoxScope.() -> Unit = {}, -) { - val counterColor = when (showWarning) { - true -> TnTTheme.colors.redColors.Red500 - false -> TnTTheme.colors.neutralColors.Neutral400 - } - - Column(modifier = modifier.fillMaxWidth()) { - Row( - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .padding(bottom = 8.dp, end = 4.dp) - .fillMaxWidth(), - ) { - Text( - text = title, - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.neutralColors.Neutral900, - ) - if (isRequired) { - Text( - text = "*", - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.redColors.Red500, - ) - } - Spacer(Modifier.weight(1f)) - - Text( - text = stringResource(R.string.text_counter, value.length, maxLength), - style = TnTTheme.typography.label1Medium, - color = counterColor, - ) - } - - TnTTextField( - value = value, - placeholder = placeholder, - onValueChange = onValueChange, - isSingleLine = isSingleLine, - showWarning = showWarning, - warningMessage = warningMessage, - keyboardType = keyboardType, - trailingComponent = trailingComponent, - modifier = Modifier.fillMaxWidth(), - ) - } -} - -@Composable -fun TnTLabeledTextField( - title: String, - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier = Modifier, - placeholder: String? = stringResource(R.string.placeholder_content_input), - enabled: Boolean = true, - isSingleLine: Boolean = false, - showWarning: Boolean = false, - isRequired: Boolean = false, - warningMessage: String? = null, - keyboardType: KeyboardType = KeyboardType.Text, - trailingComponent: @Composable BoxScope.() -> Unit = {}, -) { - Column(modifier = modifier.fillMaxWidth()) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .padding(bottom = 8.dp, end = 4.dp) - .fillMaxWidth(), - ) { - Text( - text = title, - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.neutralColors.Neutral900, - ) - if (isRequired) { - Text( - text = "*", - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.redColors.Red500, - ) - } - } - - TnTTextField( - value = value, - placeholder = placeholder, - onValueChange = onValueChange, - isSingleLine = isSingleLine, - showWarning = showWarning, - enabled = enabled, - warningMessage = warningMessage, - keyboardType = keyboardType, - trailingComponent = trailingComponent, - modifier = Modifier.fillMaxWidth(), - ) - } -} - -@Composable -fun TnTOutlinedTextField( - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier = Modifier, - placeholder: String? = stringResource(R.string.placeholder_content_input), - maxLength: Int = 15, - enabled: Boolean = true, - minHeight: Dp = 128.dp, - isError: Boolean = false, - warningMessage: String? = null, - keyboardType: KeyboardType = KeyboardType.Text, -) { - val keyboardController = LocalSoftwareKeyboardController.current - var isFocused by remember { mutableStateOf(false) } - - val borderWidth = if (isError || isFocused) 2.dp else 1.dp - - val borderColor = when { - isError -> TnTTheme.colors.redColors.Red500 - isFocused -> TnTTheme.colors.neutralColors.Neutral900 - else -> TnTTheme.colors.neutralColors.Neutral300 - } - - val counterColor = when (isError) { - true -> TnTTheme.colors.redColors.Red500 - false -> TnTTheme.colors.neutralColors.Neutral300 - } - - Column(modifier = modifier.fillMaxWidth()) { - BasicTextField( - value = value, - onValueChange = onValueChange, - enabled = enabled, - modifier = Modifier - .fillMaxWidth() - .onFocusChanged { focusState -> - isFocused = focusState.isFocused - } - .border( - width = borderWidth, - color = borderColor, - shape = RoundedCornerShape(8.dp), - ) - .defaultMinSize(minHeight = minHeight), - cursorBrush = SolidColor(TnTTheme.colors.neutralColors.Neutral900), - textStyle = TnTTheme.typography.body1Medium.copy( - color = TnTTheme.colors.neutralColors.Neutral800, - ), - decorationBox = { innerTextField -> - Box( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 12.dp), - ) { - if (value.isEmpty() && placeholder != null) { - Text( - text = placeholder, - style = TnTTheme.typography.body1Medium, - color = TnTTheme.colors.neutralColors.Neutral400, - ) - } - innerTextField() - } - }, - keyboardOptions = KeyboardOptions( - keyboardType = keyboardType, - ), - keyboardActions = KeyboardActions( - onDone = { - keyboardController?.hide() - isFocused = false - }, - ), - ) - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(vertical = 8.dp), - ) { - if (isError && warningMessage.isNullOrEmpty().not()) { - Text( - text = warningMessage ?: "", - style = TnTTheme.typography.body2Medium, - color = counterColor, - ) - } - Spacer(modifier = Modifier.weight(1f)) - Text( - text = "${value.length}/$maxLength", - style = TnTTheme.typography.label2Medium, - color = counterColor, - ) - } - } -} - -@Composable -fun TnTSelectableTextField( - title: String, - value: String?, - onValueChange: (String) -> Unit, - modifier: Modifier = Modifier, - placeholder: String? = null, - isReadOnly: Boolean = true, - isSingleLine: Boolean = false, - isRequired: Boolean = false, - showWarning: Boolean = false, - warningMessage: String? = null, - keyboardType: KeyboardType = KeyboardType.Text, - shouldClearFocus: Boolean = false, - onClick: () -> Unit, -) { - var isFocused by remember { mutableStateOf(false) } - val focusManager = LocalFocusManager.current - val interactionSource = remember { MutableInteractionSource() } - - val lineColor = when { - showWarning -> TnTTheme.colors.redColors.Red500 - isFocused -> TnTTheme.colors.neutralColors.Neutral600 - else -> TnTTheme.colors.neutralColors.Neutral200 - } - val iconColor = when { - isFocused -> TnTTheme.colors.neutralColors.Neutral600 - else -> TnTTheme.colors.neutralColors.Neutral400 - } - - LaunchedEffect(shouldClearFocus) { - if (shouldClearFocus) { - focusManager.clearFocus() - isFocused = false - } - } - - Column(modifier = modifier.fillMaxWidth()) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .padding(bottom = 8.dp, end = 4.dp) - .fillMaxWidth(), - ) { - Text( - text = title, - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.neutralColors.Neutral900, - ) - if (isRequired) { - Text( - text = "*", - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.redColors.Red500, - ) - } - } - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .clickable( - interactionSource = interactionSource, - indication = null, - onClick = { - isFocused = true - onClick() - }, - ), - ) { - BasicTextField( - value = value ?: "", - onValueChange = onValueChange, - modifier = Modifier - .weight(1f) - .padding(start = 8.dp) - .onFocusChanged { focusState -> - isFocused = focusState.isFocused - if (focusState.isFocused) { - onClick() - } - }, - readOnly = isReadOnly, - textStyle = TnTTheme.typography.body1Medium.copy( - color = if (value?.isNotEmpty() == true) { - TnTTheme.colors.neutralColors.Neutral900 - } else { - TnTTheme.colors.neutralColors.Neutral400 - }, - ), - keyboardOptions = KeyboardOptions(keyboardType = keyboardType), - cursorBrush = SolidColor(Color.Transparent), - singleLine = isSingleLine, - decorationBox = { innerTextField -> - if (value?.isEmpty() == true && placeholder != null) { - Text( - text = placeholder, - style = TnTTheme.typography.body1Medium, - color = TnTTheme.colors.neutralColors.Neutral400, - ) - } - innerTextField() - }, - ) - Box( - modifier = Modifier - .wrapContentSize(Alignment.Center) - .align(Alignment.CenterVertically) - .padding(vertical = 4.dp), - content = { - Icon( - painter = painterResource(R.drawable.ic_arrow_down), - tint = iconColor, - contentDescription = null, - ) - }, - ) - } - HorizontalDivider( - thickness = 1.dp, - color = lineColor, - ) - if (showWarning && !warningMessage.isNullOrEmpty()) { - Text( - text = warningMessage, - style = TnTTheme.typography.body2Medium, - color = TnTTheme.colors.redColors.Red500, - modifier = Modifier.padding(top = 6.dp), - ) - } - } -} - -@Preview(showBackground = true, heightDp = 100) -@Composable -private fun TnTTextFieldPreview() { - TnTTheme { - val maxLength = 15 - var text by remember { mutableStateOf("") } - var warningState by remember { mutableStateOf(false) } - - warningState = text.length > maxLength - - TnTTextField( - value = text, - onValueChange = { text = it }, - showWarning = warningState, - isSingleLine = true, - warningMessage = "${maxLength}자 이내로 입력해주세요", - modifier = Modifier - .fillMaxWidth() - .padding(4.dp), - trailingComponent = { - TnTTextButton( - text = "텍스트", - size = ButtonSize.Small, - onClick = { }, - ) - }, - ) - } -} - -@Preview(showBackground = true, heightDp = 120) -@Composable -private fun TnTLabeledTextFieldPreview() { - TnTTheme { - val maxLength = 15 - var text by remember { mutableStateOf("") } - var warningState by remember { mutableStateOf(false) } - - warningState = text.length > maxLength - - TnTLabeledTextField( - title = "제목", - value = text, - onValueChange = { text = it }, - showWarning = warningState, - isSingleLine = true, - isRequired = false, - warningMessage = "${maxLength}자 이내로 입력해주세요", - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - trailingComponent = { - TnTTextButton( - text = "텍스트", - size = ButtonSize.Small, - onClick = { }, - ) - }, - ) - } -} - -@Preview(showBackground = true, heightDp = 120) -@Composable -private fun TnTLabeledTextFieldWithCounterPreview() { - TnTTheme { - val maxLength = 15 - var text by remember { mutableStateOf("") } - var warningState by remember { mutableStateOf(false) } - - warningState = text.length > maxLength - - TnTLabeledTextFieldWithCounter( - title = "제목", - value = text, - onValueChange = { text = it }, - maxLength = maxLength, - showWarning = warningState, - isSingleLine = true, - isRequired = false, - warningMessage = "${maxLength}자 이내로 입력해주세요", - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - trailingComponent = { - TnTTextButton( - text = "텍스트", - size = ButtonSize.Small, - onClick = { }, - ) - }, - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun TnTOutlinedTextFieldPreview() { - TnTTheme { - val maxLength = 100 - var text by remember { mutableStateOf("") } - var warningState by remember { mutableStateOf(false) } - - warningState = text.length > maxLength - - TnTOutlinedTextField( - value = text, - onValueChange = { text = it }, - maxLength = maxLength, - isError = warningState, - warningMessage = "에러", - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun TnTClickableTextFieldPreview() { - var date by remember { mutableStateOf("") } - var shouldClearFocus by remember { mutableStateOf(false) } - - fun updateValue() { - date = LocalDate.of(2025, 3, 3).toString() - shouldClearFocus = true - } - - fun onClickComponent() { - shouldClearFocus = false - updateValue() - } - - TnTTheme { - TnTSelectableTextField( - title = "제목", - value = date, - placeholder = "2025-01-01", - isRequired = true, - isSingleLine = true, - onClick = { onClickComponent() }, - onValueChange = { }, - shouldClearFocus = shouldClearFocus, - modifier = Modifier.padding(10.dp), - ) - } -} diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt index 3e06ea0a..cce5a002 100644 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt @@ -32,7 +32,7 @@ import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize import co.kr.tnt.designsystem.theme.TnTTheme @Composable -fun TnTLabeledTextField2( +fun TnTLabeledTextField( value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, @@ -68,7 +68,7 @@ fun TnTLabeledTextField2( } Spacer(modifier = Modifier.height(8.dp)) } - TnTTextField2( + TnTTextField( value = value, onValueChange = onValueChange, modifier = Modifier.fillMaxWidth(), @@ -136,7 +136,7 @@ fun TnTLabeledTextFieldWithTextButton( trailingButtonEnabled: Boolean = true, onClickTextField: (() -> Unit)? = null, ) { - TnTLabeledTextField2( + TnTLabeledTextField( value = value, onValueChange = onValueChange, modifier = modifier, @@ -172,7 +172,7 @@ fun TnTLabeledTextFieldWithTextButton( @Composable private fun TnTLabeledTextField2EmptyPreview() { TnTTheme { - TnTLabeledTextField2( + TnTLabeledTextField( title = "이름", value = "", onValueChange = {}, @@ -185,7 +185,7 @@ private fun TnTLabeledTextField2EmptyPreview() { @Composable private fun TnTLabeledTextField2WithTextPreview() { TnTTheme { - TnTLabeledTextField2( + TnTLabeledTextField( title = "이름", value = "홍길동", onValueChange = {}, @@ -198,7 +198,7 @@ private fun TnTLabeledTextField2WithTextPreview() { @Composable private fun TnTLabeledTextField2RequiredPreview() { TnTTheme { - TnTLabeledTextField2( + TnTLabeledTextField( title = "이름", value = "", onValueChange = {}, @@ -212,7 +212,7 @@ private fun TnTLabeledTextField2RequiredPreview() { @Composable private fun TnTLabeledTextField2ErrorPreview() { TnTTheme { - TnTLabeledTextField2( + TnTLabeledTextField( title = "이름", value = "잘못된 입력", onValueChange = {}, @@ -228,7 +228,7 @@ private fun TnTLabeledTextField2ErrorPreview() { @Composable private fun TnTLabeledTextField2LargePreview() { TnTTheme { - TnTLabeledTextField2( + TnTLabeledTextField( title = "메모", value = "여러 줄의 텍스트를\n입력할 수 있습니다.", onValueChange = {}, @@ -244,7 +244,7 @@ private fun TnTLabeledTextField2WithCounterEmptyPreview() { TnTTheme { var text by remember { mutableStateOf("") } - TnTLabeledTextField2( + TnTLabeledTextField( title = "닉네임", value = text, onValueChange = { text = it }, @@ -260,7 +260,7 @@ private fun TnTLabeledTextField2WithCounterTextPreview() { TnTTheme { var text by remember { mutableStateOf("홍길동") } - TnTLabeledTextField2( + TnTLabeledTextField( title = "닉네임", value = text, onValueChange = { text = it }, @@ -277,7 +277,7 @@ private fun TnTLabeledTextField2WithCounterErrorPreview() { TnTTheme { var text by remember { mutableStateOf("매우 긴 닉네임 매우 긴 닉네임") } - TnTLabeledTextField2( + TnTLabeledTextField( title = "닉네임", value = text, onValueChange = { text = it }, @@ -317,14 +317,14 @@ private fun TnTLabeledTextField2AllStatesPreview() { verticalArrangement = Arrangement.spacedBy(24.dp), ) { Text("Basic", style = TnTTheme.typography.body1Bold) - TnTLabeledTextField2( + TnTLabeledTextField( title = "이름", value = "홍길동", onValueChange = {}, ) Text("Required", style = TnTTheme.typography.body1Bold) - TnTLabeledTextField2( + TnTLabeledTextField( title = "이메일", value = "", onValueChange = {}, @@ -332,7 +332,7 @@ private fun TnTLabeledTextField2AllStatesPreview() { ) Text("Error with message", style = TnTTheme.typography.body1Bold) - TnTLabeledTextField2( + TnTLabeledTextField( title = "전화번호", value = "잘못된 형식", onValueChange = {}, @@ -342,7 +342,7 @@ private fun TnTLabeledTextField2AllStatesPreview() { ) Text("With Counter", style = TnTTheme.typography.body1Bold) - TnTLabeledTextField2( + TnTLabeledTextField( title = "닉네임", value = "홍길동", onValueChange = {}, @@ -361,7 +361,7 @@ private fun TnTLabeledTextField2AllStatesPreview() { ) Text("Large size", style = TnTTheme.typography.body1Bold) - TnTLabeledTextField2( + TnTLabeledTextField( title = "메모", value = "여러 줄의\n텍스트 입력", onValueChange = {}, diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField_2.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField.kt similarity index 94% rename from core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField_2.kt rename to core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField.kt index ab4d4606..dbe45b4a 100644 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField_2.kt +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/SelectableTextField.kt @@ -25,7 +25,7 @@ import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize import co.kr.tnt.designsystem.theme.TnTTheme @Composable -fun TnTSelectableTextField2( +fun TnTSelectableTextField( value: String, onClick: () -> Unit, modifier: Modifier = Modifier, @@ -43,7 +43,7 @@ fun TnTSelectableTextField2( } } - TnTTextField2( + TnTTextField( value = value, onValueChange = { }, modifier = modifier, @@ -63,7 +63,7 @@ fun TnTSelectableTextField2( } @Composable -fun TnTSelectableLabeledTextField2( +fun TnTSelectableLabeledTextField( value: String, onClickTextField: () -> Unit, modifier: Modifier = Modifier, @@ -75,7 +75,7 @@ fun TnTSelectableLabeledTextField2( warningMessage: String? = null, trailing: @Composable (RowScope.() -> Unit)? = null, ) { - TnTLabeledTextField2( + TnTLabeledTextField( value = value, onValueChange = { }, modifier = modifier, @@ -113,7 +113,7 @@ fun TnTSelectableLabeledTextFieldWithTextButton( trailingButtonSize: ButtonSize = ButtonSize.Small, trailingButtonType: ButtonType = ButtonType.Primary, ) { - TnTSelectableLabeledTextField2( + TnTSelectableLabeledTextField( value = value, onClickTextField = onClickTextField, modifier = modifier, @@ -161,20 +161,20 @@ private fun TnTSelectableTextField2AllStatesPreview() { verticalArrangement = Arrangement.spacedBy(24.dp), ) { Text("Empty", style = TnTTheme.typography.body1Bold) - TnTSelectableTextField2( + TnTSelectableTextField( value = "", onClick = {}, placeholder = "선택해주세요", ) Text("Selected", style = TnTTheme.typography.body1Bold) - TnTSelectableTextField2( + TnTSelectableTextField( value = "2025/01/15", onClick = {}, ) Text("Labeled - Required", style = TnTTheme.typography.body1Bold) - TnTSelectableLabeledTextField2( + TnTSelectableLabeledTextField( value = "", onClickTextField = {}, title = "날짜", @@ -183,7 +183,7 @@ private fun TnTSelectableTextField2AllStatesPreview() { ) Text("Labeled - Selected", style = TnTTheme.typography.body1Bold) - TnTSelectableLabeledTextField2( + TnTSelectableLabeledTextField( value = "09:00", onClickTextField = {}, title = "시간", @@ -191,7 +191,7 @@ private fun TnTSelectableTextField2AllStatesPreview() { ) Text("Labeled - Warning", style = TnTTheme.typography.body1Bold) - TnTSelectableLabeledTextField2( + TnTSelectableLabeledTextField( value = "", onClickTextField = {}, title = "식사 유형", diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField_2.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField.kt similarity index 99% rename from core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField_2.kt rename to core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField.kt index 9f884117..64382cb5 100644 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField_2.kt +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/TextField.kt @@ -40,7 +40,7 @@ import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldType import co.kr.tnt.designsystem.theme.TnTTheme @Composable -fun TnTTextField2( +fun TnTTextField( value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, @@ -215,7 +215,7 @@ private class TextFieldStateProvider : PreviewParameterProvider Unit, ) { - TnTSelectableLabeledTextField2( + TnTSelectableLabeledTextField( title = stringResource(R.string.meal_date), value = dateFormatter.format(date, "yyyy/MM/dd"), showRequiredTitleBadge = true, @@ -429,7 +429,7 @@ private fun MealTime( onClick: () -> Unit, ) { val now = LocalTime.now() - TnTSelectableLabeledTextField2( + TnTSelectableLabeledTextField( title = stringResource(R.string.meal_time), value = time?.let { dateFormatter.format(it, "HH:mm") } ?: "", showRequiredTitleBadge = true, diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt index ee88e7f2..944cb87a 100644 --- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt +++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt @@ -32,8 +32,8 @@ import co.kr.tnt.core.ui.R.string.core_weight_label import co.kr.tnt.core.ui.R.string.core_weight_unit import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton -import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 -import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField +import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextField import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.feature.trainee.signup.R @@ -78,7 +78,7 @@ internal fun TraineeBasicInfoPage( subTitle = stringResource(R.string.basic_info_for_trainer), ) Spacer(Modifier.padding(top = 48.dp)) - TnTSelectableLabeledTextField2( + TnTSelectableLabeledTextField( modifier = Modifier.padding(horizontal = 20.dp), value = state.birthday?.format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) ?: stringResource(R.string.birthday_placeholder), @@ -113,7 +113,7 @@ internal fun TraineeBasicInfoPage( .fillMaxWidth() .padding(horizontal = 20.dp), ) { - TnTLabeledTextField2( + TnTLabeledTextField( title = stringResource(core_height_label), value = state.height ?: "", placeholder = "0", @@ -129,7 +129,7 @@ internal fun TraineeBasicInfoPage( onValueChange = onChangeHeight, modifier = Modifier.weight(1f), ) - TnTLabeledTextField2( + TnTLabeledTextField( title = stringResource(core_weight_label), value = state.weight ?: "", placeholder = "00.0", diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt index f7c7d26c..4403ef99 100644 --- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt +++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt @@ -30,7 +30,7 @@ import co.kr.tnt.core.ui.R.string.core_text_length_and_format_warning import co.kr.tnt.designsystem.component.TnTProfileImage import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton -import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.domain.UserProfilePolicy import co.kr.tnt.feature.trainee.signup.R @@ -99,7 +99,7 @@ internal fun TraineeProfileSetupPage( }, ) Spacer(Modifier.padding(top = 60.dp)) - TnTLabeledTextField2( + TnTLabeledTextField( title = stringResource(core_name), value = state.name, onValueChange = { newValue -> diff --git a/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt b/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt index cb289277..5e3cf4ea 100644 --- a/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt +++ b/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt @@ -69,8 +69,8 @@ import co.kr.tnt.designsystem.component.calendar.TnTCalendarSelector import co.kr.tnt.designsystem.component.calendar.TnTMonthCalendar import co.kr.tnt.designsystem.component.calendar.model.DayState import co.kr.tnt.designsystem.component.calendar.utils.rememberMostVisibleMonth -import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 -import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField +import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextField import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize import co.kr.tnt.designsystem.snackbar.LocalSnackbar import co.kr.tnt.designsystem.theme.TnTTheme @@ -324,7 +324,7 @@ private fun Selector( modifier: Modifier = Modifier, isWarning: Boolean = false, ) { - TnTSelectableLabeledTextField2( + TnTSelectableLabeledTextField( title = title, showRequiredTitleBadge = true, value = value, @@ -481,7 +481,7 @@ private fun Memo( isWarning: Boolean, onValueChanged: (String) -> Unit, ) { - TnTLabeledTextField2( + TnTLabeledTextField( title = stringResource(R.string.write_memo), value = value, onValueChange = onValueChanged, diff --git a/feature/trainer/modifymyinfo/src/main/java/co/kr/tnt/trainer/modifymyinfo/TrainerModifyMyInfo.kt b/feature/trainer/modifymyinfo/src/main/java/co/kr/tnt/trainer/modifymyinfo/TrainerModifyMyInfo.kt index e5d0dfa4..75a1f645 100644 --- a/feature/trainer/modifymyinfo/src/main/java/co/kr/tnt/trainer/modifymyinfo/TrainerModifyMyInfo.kt +++ b/feature/trainer/modifymyinfo/src/main/java/co/kr/tnt/trainer/modifymyinfo/TrainerModifyMyInfo.kt @@ -33,7 +33,7 @@ import co.kr.tnt.designsystem.component.TnTIconPopupDialog import co.kr.tnt.designsystem.component.TnTProfileImage import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton -import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField import co.kr.tnt.designsystem.snackbar.LocalSnackbar import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.domain.UserProfilePolicy @@ -142,7 +142,7 @@ private fun TrainerModifyMyInfoScreen( }, ) Spacer(modifier = Modifier.height(48.dp)) - TnTLabeledTextField2( + TnTLabeledTextField( title = stringResource(core_name), value = state.name, onValueChange = { newValue -> diff --git a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt index dc2474a6..66dc53cd 100644 --- a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt +++ b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt @@ -32,7 +32,7 @@ import co.kr.tnt.core.ui.R.string.core_text_length_and_format_warning import co.kr.tnt.designsystem.component.TnTProfileImage import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton -import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField2 +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.domain.UserProfilePolicy import co.kr.tnt.feature.trainer.signup.R @@ -102,7 +102,7 @@ internal fun TrainerProfileSetupPage( }, ) Spacer(Modifier.padding(top = 60.dp)) - TnTLabeledTextField2( + TnTLabeledTextField( title = stringResource(core_name), value = state.name, onValueChange = { newValue -> From d8436b846c3902943c255572245b6a24d078ff4c Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Thu, 20 Nov 2025 22:17:57 +0900 Subject: [PATCH 11/14] =?UTF-8?q?[TNT-000]=20fix:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=84=88=20=EC=88=98=EC=97=85=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=82=B4=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EA=B0=84=20=EA=B0=84=EA=B2=A9=20=EC=A1=B0=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=EB=A6=AC=EC=86=8C?= =?UTF-8?q?=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/tnt/trainer/addptsession/AddPtSessionScreen.kt | 11 ++++++----- .../addptsession/src/main/res/values/strings.xml | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt b/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt index 5e3cf4ea..e3ba1a1d 100644 --- a/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt +++ b/feature/trainer/addptsession/src/main/java/co/kr/tnt/trainer/addptsession/AddPtSessionScreen.kt @@ -242,14 +242,14 @@ private fun AddPtSessionScreen( ) { Spacer(modifier = Modifier.height(16.dp)) Description() - Spacer(modifier = Modifier.height(48.dp)) + Spacer(modifier = Modifier.height(20.dp)) Selector( title = stringResource(R.string.select_member), value = state.selectedMember?.traineeName ?: "", placeholder = stringResource(R.string.insert_memeber), onClick = onClickMember, ) - Spacer(modifier = Modifier.height(48.dp)) + Spacer(modifier = Modifier.height(32.dp)) Selector( title = stringResource(R.string.pt_date), value = state.selectedDate?.let { selectedDate -> @@ -258,7 +258,7 @@ private fun AddPtSessionScreen( placeholder = stringResource(R.string.insert_date), onClick = onClickDate, ) - Spacer(modifier = Modifier.height(48.dp)) + Spacer(modifier = Modifier.height(32.dp)) TimeSelector( startTime = state.selectedStartTime, endTime = state.selectedEndTime, @@ -268,7 +268,7 @@ private fun AddPtSessionScreen( isWarning = state.isErrorTime, ) if (state.selectedStartTime != null && state.selectedEndTime == null) { - Spacer(modifier = Modifier.height(48.dp)) + Spacer(modifier = Modifier.height(32.dp)) MinuteChips( selectedMinute = state.totalSessionMinute ?: 0, onClickChip = onClickMinuteChip, @@ -280,7 +280,7 @@ private fun AddPtSessionScreen( minute = totalSessionMinute, ) } - Spacer(modifier = Modifier.height(48.dp)) + Spacer(modifier = Modifier.height(32.dp)) Memo( value = state.memo, isWarning = state.isErrorMemo, @@ -484,6 +484,7 @@ private fun Memo( TnTLabeledTextField( title = stringResource(R.string.write_memo), value = value, + placeholder = stringResource(R.string.memo_for_pt_session), onValueChange = onValueChanged, modifier = Modifier.fillMaxWidth(), size = TnTTextFieldSize.LARGE, diff --git a/feature/trainer/addptsession/src/main/res/values/strings.xml b/feature/trainer/addptsession/src/main/res/values/strings.xml index ea6d4ed2..42f8f58b 100644 --- a/feature/trainer/addptsession/src/main/res/values/strings.xml +++ b/feature/trainer/addptsession/src/main/res/values/strings.xml @@ -23,4 +23,5 @@ 등록된 일정은 트레이니에게도 표시돼요! "총 " " 수업이에요" + PT 수업에서 기억해야 할 것을 메모해보세요 From 27d91aa009e2a812eca5e27f53ad75211f16bdd5 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Thu, 20 Nov 2025 22:20:02 +0900 Subject: [PATCH 12/14] =?UTF-8?q?[TNT-309]=20fix:=20LabeledTextField=20?= =?UTF-8?q?=EB=82=B4=20=EC=B9=B4=EC=9A=B4=ED=8C=85=20=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B3=80=EA=B2=BD=20(0=EC=9E=90/100=EC=9E=90=20->?= =?UTF-8?q?=200=20/=20100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tnt/designsystem/component/textfield/LabeledTextField.kt | 4 +--- core/designsystem/src/main/res/values/strings.xml | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt index cce5a002..e28624ba 100644 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/textfield/LabeledTextField.kt @@ -20,11 +20,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import co.kr.tnt.core.designsystem.R import co.kr.tnt.designsystem.component.button.TnTTextButton import co.kr.tnt.designsystem.component.button.model.ButtonSize import co.kr.tnt.designsystem.component.button.model.ButtonType @@ -100,7 +98,7 @@ fun TnTLabeledTextField( if (maxLength != null) { Spacer(modifier = Modifier.weight(1f)) Text( - text = stringResource(R.string.text_counter, value.length, maxLength), + text = "${value.length} / $maxLength", style = TnTTheme.typography.label1Medium, color = when { isWarning -> TnTTheme.colors.redColors.Red500 diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index 50ef6dce..e7310f36 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -5,7 +5,6 @@ 오후 - %1$d/%2$d자 내용을 입력해주세요 From 0fec7313c48de03a8b1cccb92ebf9022ac0add37 Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Thu, 20 Nov 2025 22:21:58 +0900 Subject: [PATCH 13/14] =?UTF-8?q?[TNT-309]=20fix:=20=EC=8B=A0=EA=B7=9C=20T?= =?UTF-8?q?extField=20=EA=B0=80=20=EC=A0=81=EC=9A=A9=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=80=20=ED=99=94=EB=A9=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 트레이너에게 주의사항 전달 화면 * 트레이니 식단 기록 화면 --- .../mealrecord/TraineeMealRecordScreen.kt | 30 +++++-------------- .../signup/TraineeNoteForTrainerPage.kt | 14 +++++---- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordScreen.kt b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordScreen.kt index e6f80a01..c2862af2 100644 --- a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordScreen.kt +++ b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/TraineeMealRecordScreen.kt @@ -58,7 +58,6 @@ import co.kr.tnt.core.ui.R.string.core_ok import co.kr.tnt.designsystem.component.TnTBottomSheetDialog import co.kr.tnt.designsystem.component.TnTDivider import co.kr.tnt.designsystem.component.TnTIconPopupDialog -import co.kr.tnt.designsystem.component.TnTOutlinedTextField import co.kr.tnt.designsystem.component.TnTSingleButtonPopupDialog import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.TnTWheelTimePicker @@ -69,7 +68,9 @@ import co.kr.tnt.designsystem.component.calendar.TnTCalendarSelector import co.kr.tnt.designsystem.component.calendar.TnTMonthCalendar import co.kr.tnt.designsystem.component.calendar.model.DayState import co.kr.tnt.designsystem.component.calendar.utils.rememberMostVisibleMonth +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField import co.kr.tnt.designsystem.component.textfield.TnTSelectableLabeledTextField +import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize import co.kr.tnt.designsystem.snackbar.LocalSnackbar import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.domain.UserProfilePolicy @@ -491,30 +492,15 @@ private fun MealMemo( onValueChange: (String) -> Unit, ) { Column { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth(), - ) { - Text( - text = stringResource(R.string.wirte_memo), - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.neutralColors.Neutral900, - ) - Text( - text = "*", - style = TnTTheme.typography.body1Bold, - color = TnTTheme.colors.redColors.Red500, - ) - } - Spacer(Modifier.height(10.dp)) - TnTOutlinedTextField( + TnTLabeledTextField( + title = stringResource(R.string.wirte_memo), value = state.memo, - onValueChange = { newValue -> - onValueChange(newValue) - }, + onValueChange = onValueChange, + showRequiredTitleBadge = true, + size = TnTTextFieldSize.LARGE, placeholder = stringResource(R.string.enter_meal_info), maxLength = 100, - isError = state.showWarning, + isWarning = state.showWarning, warningMessage = stringResource(core_length_warning, 100), ) } diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeNoteForTrainerPage.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeNoteForTrainerPage.kt index 329a4978..baa3a6eb 100644 --- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeNoteForTrainerPage.kt +++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeNoteForTrainerPage.kt @@ -17,10 +17,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import co.kr.tnt.core.designsystem.R.string.placeholder_content_input import co.kr.tnt.core.ui.R.string.core_next -import co.kr.tnt.designsystem.component.TnTOutlinedTextField import co.kr.tnt.designsystem.component.TnTTopBarWithBackButton import co.kr.tnt.designsystem.component.button.TnTBottomButton +import co.kr.tnt.designsystem.component.textfield.TnTLabeledTextField +import co.kr.tnt.designsystem.component.textfield.model.TnTTextFieldSize import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.feature.trainee.signup.R import co.kr.tnt.trainee.signup.component.ProgressSteps @@ -57,13 +59,13 @@ internal fun TraineeNoteForTrainerPage( subTitle = stringResource(R.string.let_the_trainer_know), ) Spacer(Modifier.padding(top = 48.dp)) - TnTOutlinedTextField( + TnTLabeledTextField( value = caution ?: "", - onValueChange = { newValue -> - onChangeCaution(newValue) - }, + onValueChange = onChangeCaution, modifier = Modifier.padding(horizontal = 20.dp), - isError = (caution?.length ?: 0) >= MAX_LENGTH, + size = TnTTextFieldSize.LARGE, + placeholder = stringResource(placeholder_content_input), + isWarning = (caution?.length ?: 0) >= MAX_LENGTH, warningMessage = stringResource(R.string.text_length_overflow), maxLength = 100, ) From 56524d87c0fe892dccfcec35726d1e01256c246e Mon Sep 17 00:00:00 2001 From: hoyahozz <85336456+hoyahozz@users.noreply.github.com> Date: Thu, 20 Nov 2025 22:22:49 +0900 Subject: [PATCH 14/14] =?UTF-8?q?[TNT-000]=20fix:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=8B=88=20=EA=B8=B0=EB=B3=B8=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=ED=99=94=EB=A9=B4=20=EB=82=B4=20[?= =?UTF-8?q?=EC=83=9D=EB=85=84=EC=9B=94=EC=9D=BC]=20placeholder=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt index 944cb87a..73170737 100644 --- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt +++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeBasicInfoPage.kt @@ -80,8 +80,8 @@ internal fun TraineeBasicInfoPage( Spacer(Modifier.padding(top = 48.dp)) TnTSelectableLabeledTextField( modifier = Modifier.padding(horizontal = 20.dp), - value = state.birthday?.format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) - ?: stringResource(R.string.birthday_placeholder), + value = state.birthday?.format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) ?: "", + placeholder = stringResource(R.string.birthday_placeholder), onClickTextField = { DatePickerDialog( context,