Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions app/src/main/kotlin/com/gamss/android/app/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.gamss.android.app

import android.graphics.Color
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import com.gamss.android.app.navigation.GamssRootNavHost
Expand All @@ -13,9 +15,14 @@ class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// 시스템 바 아이콘과 테마를 모두 라이트로 고정한다. 둘 중 하나만 고정하면 다크 모드 기기에서
// 검은 배경에 검은 아이콘이 겹친다. 다크 시안이 나오면 이 고정과 MainScreen 의 고정을 함께 푼다.
enableEdgeToEdge(
statusBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.BLACK),
navigationBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.BLACK),
)
setContent {
GamssTheme {
GamssTheme(darkTheme = false) {
GamssRootNavHost()
}
}
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-night/themes.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.GAMSS" parent="android:Theme.Material.NoActionBar" />
<!-- 셸이 라이트로 고정된 동안에는 첫 프레임 윈도우 배경도 라이트여야 한다. 다크 시안이 나오면 되돌린다. -->
<style name="Theme.GAMSS" parent="android:Theme.Material.Light.NoActionBar" />
</resources>
Original file line number Diff line number Diff line change
@@ -1,27 +1,46 @@
package com.gamss.android.core.designsystem.textfield

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.gamss.android.core.designsystem.R
import com.gamss.android.core.designsystem.theme.GamssTheme

/**
* 라벨 텍스트 + [OutlinedTextField] 조합의 입력 필드.
* 라벨 텍스트 + 입력 필드 조합의 입력 필드.
* 라벨이 필드 위에 별도로 표시되는 형태
*
* 시안의 필드는 높이가 52dp라, 최소 높이 56dp에 자체 여백을 가진
* [androidx.compose.material3.OutlinedTextField] 대신 [BasicTextField]로 그린다.
*
* 테두리는 손그림 시안이라 선 두께로 그릴 수 없어 `bg_textfield_default` 벡터를 깔고
* gray950 으로 틴트한다. 덕분에 다크 모드에서도 색만 반전된다.
*/
@Composable
fun GamssTextField(
label: String,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
placeholder: String? = null,
errorMessage: String? = null,
singleLine: Boolean = true,
) {
Column(modifier = modifier) {
Expand All @@ -31,29 +50,98 @@ fun GamssTextField(
color = GamssTheme.colors.gray950,
)
Spacer(modifier = Modifier.height(GamssTheme.spacing.spacing200))
OutlinedTextField(
BasicTextField(
modifier = Modifier.fillMaxWidth(),
value = value,
onValueChange = onValueChange,
singleLine = singleLine,
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = GamssTheme.colors.gray200,
unfocusedBorderColor = GamssTheme.colors.gray200,
),
)
textStyle = GamssTheme.typography.body3Medium.copy(color = GamssTheme.colors.gray950),
cursorBrush = SolidColor(GamssTheme.colors.gray950),
) { innerTextField ->
Box(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = FieldMinHeight)
.background(GamssTheme.colors.gray025),
contentAlignment = Alignment.CenterStart,
) {
// matchParentSize 는 부모 크기 계산에 참여하지 않아, 필드 높이는 그대로 내용이
// 정하고 테두리만 정해진 크기에 맞춰 늘어난다. Modifier.paint 는 반대로
// 남은 공간을 꽉 채우도록 제약을 고정해 버려 쓸 수 없다.
Image(
modifier = Modifier.matchParentSize(),
painter = painterResource(R.drawable.bg_textfield_default),
contentDescription = null,
contentScale = ContentScale.FillBounds,
colorFilter = ColorFilter.tint(GamssTheme.colors.gray950),
)
Box(modifier = Modifier.padding(horizontal = GamssTheme.spacing.spacing300)) {
if (placeholder != null && value.isEmpty()) {
Text(
text = placeholder,
style = GamssTheme.typography.body3Medium,
color = GamssTheme.colors.gray400,
)
}
innerTextField()
}
}
}
if (errorMessage != null) {
Spacer(modifier = Modifier.height(GamssTheme.spacing.spacing100))
Text(
text = errorMessage,
style = GamssTheme.typography.body5Medium,
color = GamssTheme.colors.red,
)
}
}
}

private val FieldMinHeight = 52.dp

@Preview(name = "GamssTextField", showBackground = true)
@Composable
@Suppress("UnusedPrivateMember")
private fun GamssTextFieldPreview() {
GamssTheme {
GamssTextField(
modifier = Modifier.fillMaxWidth(),
label = "닉네임 변경",
label = "변경할 닉네임을 입력해주세요.",
placeholder = "닉네임은 2~10자 사이로 입력해주세요.",
value = "송지연",
onValueChange = {},
)
}
}

@Preview(name = "GamssTextField - Placeholder", showBackground = true)
@Composable
@Suppress("UnusedPrivateMember")
private fun GamssTextFieldPlaceholderPreview() {
GamssTheme {
GamssTextField(
modifier = Modifier.fillMaxWidth(),
label = "변경할 닉네임을 입력해주세요.",
placeholder = "닉네임은 2~10자 사이로 입력해주세요.",
value = "",
onValueChange = {},
)
}
}

@Preview(name = "GamssTextField - Error", showBackground = true)
@Composable
@Suppress("UnusedPrivateMember")
private fun GamssTextFieldErrorPreview() {
GamssTheme {
GamssTextField(
modifier = Modifier.fillMaxWidth(),
label = "변경할 닉네임을 입력해주세요.",
placeholder = "닉네임은 2~10자 사이로 입력해주세요.",
errorMessage = "닉네임은 2자 이상으로 입력해주세요.",
value = "아",
onValueChange = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ internal val ChromaticDarkPurple = Color(0xFFCC6DFC)

@Immutable
data class GamssColors(
// 기기 다크 모드가 아니라 GamssTheme 이 고른 팔레트를 가리킨다. 셸이 라이트로 고정된 동안
// isSystemInDarkTheme() 을 직접 보면 테마와 어긋나므로, 다크 분기는 이 값으로 판단한다.
val isDark: Boolean,
val white: Color,
val black: Color,
// Figma 의 Gray/Gray1000. 라이트와 다크가 같은 값이라 gray025~gray950 과 달리 반전하지 않는다.
Expand Down Expand Up @@ -88,6 +91,7 @@ data class GamssColors(
)

val LightGamssColors = GamssColors(
isDark = false,
white = White,
black = Black,
gray1000 = Gray1000,
Expand Down Expand Up @@ -115,6 +119,7 @@ val LightGamssColors = GamssColors(
)

val DarkGamssColors = GamssColors(
isDark = true,
white = White,
black = Black,
gray1000 = Gray1000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,20 @@ fun GamssTheme(
content: @Composable () -> Unit,
) {
val colors = if (darkTheme) DarkGamssColors else LightGamssColors
// 디자인 시스템에 primary 토큰이 없어 Primary 버튼과 같은 gray950 을 쓴다.
// CircularProgressIndicator 등 Material 기본값이 이 색을 참조한다.
val materialColorScheme = if (darkTheme) {
darkColorScheme(background = colors.gray025)
darkColorScheme(
background = colors.gray025,
primary = colors.gray950,
onPrimary = colors.gray025,
)
} else {
lightColorScheme(background = colors.gray025)
lightColorScheme(
background = colors.gray025,
primary = colors.gray950,
onPrimary = colors.gray025,
)
}

CompositionLocalProvider(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,7 @@ sealed interface GamssTopNavigationContent {
fun GamssTopNavigation(
content: GamssTopNavigationContent,
modifier: Modifier = Modifier,
backgroundColor: Color = if (GamssTheme.isDarkTheme) {
GamssTheme.colors.black
} else {
GamssTheme.colors.white
},
backgroundColor: Color = GamssTheme.colors.background,
showLeftIcon: Boolean = false,
leftIconContentDescription: String? = null,
onLeftIconClick: () -> Unit = {},
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ package com.gamss.android.domain.user
*/
object NicknamePolicy {
const val MIN_LENGTH = 2
const val MAX_LENGTH = 20
const val MAX_LENGTH = 10
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ internal fun ChattingListTopBar(
} else {
GamssTopNavigationContent.Logo
},
backgroundColor = GamssTheme.colors.background,
showLeftIcon = isSelectionMode,
leftIconContentDescription = stringResource(R.string.chatting_list_selection_cancel),
onLeftIconClick = onSelectionCancel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ private fun OnboardingTopBar(
Box(modifier = modifier.fillMaxWidth()) {
GamssTopNavigation(
content = GamssTopNavigationContent.None,
backgroundColor = GamssTheme.colors.background,
showLeftIcon = showBack,
leftIconContentDescription = stringResource(R.string.onboarding_back_description),
onLeftIconClick = onBackClick,
Expand Down
1 change: 1 addition & 0 deletions feature/setting/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ dependencies {
implementation(projects.core.common)
implementation(projects.core.ui)
implementation(projects.domain)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.compose.material.icons.core)
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ fun AccountInfoScreen(
Column(
modifier = modifier
.fillMaxSize()
.background(GamssTheme.colors.gray025),
.background(GamssTheme.colors.background),
) {
GamssTopNavigation(
modifier = Modifier.padding(bottom = GamssTheme.spacing.spacing150),
modifier = Modifier.padding(bottom = GamssTheme.spacing.spacing300),
title = stringResource(R.string.setting_list_user_account),
showLeftIcon = true,
onLeftIconClick = onBackClick,
Expand Down
Loading
Loading