From 8f63862de3a4ef53aeab1b9b1aee1f46ad815a91 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Thu, 22 Jan 2026 21:56:33 +0900 Subject: [PATCH 01/21] =?UTF-8?q?refactor:=20PrezelTheme=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EB=B0=8F=20=EC=83=89=EC=83=81=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelTheme`의 `CompositionLocalProvider`에 `LocalTextStyle`과 `LocalContentColor`를 추가하여 앱 전체의 기본 텍스트 스타일과 콘텐츠 색상을 명시적으로 설정했습니다. * `LocalTextStyle`에 `typographyScheme.body3Regular`를 적용하여 기본 텍스트 스타일을 지정했습니다. * `LocalContentColor`에 `colorScheme.textLarge`를 적용하여 기본 콘텐츠 색상을 지정했습니다. --- .../com/team/prezel/core/designsystem/theme/PrezelTheme.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelTheme.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelTheme.kt index 88fdd41f..5871eb49 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelTheme.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelTheme.kt @@ -1,6 +1,8 @@ package com.team.prezel.core.designsystem.theme import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.ReadOnlyComposable @@ -66,6 +68,8 @@ fun PrezelTheme( LocalPrezelShapes provides PrezelShapes, LocalPrezelSpacing provides PrezelSpacing, LocalPrezelStroke provides PrezelStroke, + LocalTextStyle provides typographyScheme.body3Regular, + LocalContentColor provides colorScheme.textLarge, content = content, ) } From 2d4f27d934cabf57eff48784e9c301fad35b1d72 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 01:42:04 +0900 Subject: [PATCH 02/21] =?UTF-8?q?fix:=20Light=20=ED=85=8C=EB=A7=88?= =?UTF-8?q?=EC=9D=98=20`borderSmall`=20=EC=83=89=EC=83=81=20=EA=B0=92=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 `LightPrezelColorScheme`에서 `borderSmall`의 색상 값을 `CoolGray500`에서 `CoolGray50`으로 변경했습니다. --- .../team/prezel/core/designsystem/theme/PrezelColorScheme.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelColorScheme.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelColorScheme.kt index db684b31..c2e505ef 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelColorScheme.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelColorScheme.kt @@ -43,7 +43,7 @@ internal object PrezelColorScheme { iconMedium = ColorTokens.CoolGray800, iconLarge = ColorTokens.CoolGray900, iconDisabled = ColorTokens.CoolGray300, - borderSmall = ColorTokens.CoolGray500, + borderSmall = ColorTokens.CoolGray50, borderRegular = ColorTokens.CoolGray100, borderMedium = ColorTokens.CoolGray200, borderLarge = ColorTokens.CoolGray500, From 4c4d49bfed5a7d4cb4b6432d79879ecb21d79eb0 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 01:46:29 +0900 Subject: [PATCH 03/21] =?UTF-8?q?feat:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20PrezelButton=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 디자인 시스템의 공통 버튼 컴포넌트인 `PrezelButton`을 추가했습니다. 이 컴포넌트는 타입, 계층, 크기, 모양 등 다양한 조합을 통해 유연하게 사용할 수 있도록 설계되었습니다. * **주요 기능 및 속성** * `PrezelButtonType`: `FILLED`, `OUTLINED`, `GHOST` 3가지 버튼 타입을 지원합니다. * `PrezelButtonHierarchy`: `PRIMARY`, `SECONDARY` 2가지 계층을 제공합니다. * `PrezelButtonSize`: `XSMALL`, `SMALL`, `REGULAR` 3가지 크기 옵션을 포함합니다. * `isRounded` 파라미터를 통해 버튼의 모서리를 둥글게 또는 직각으로 설정할 수 있습니다. * `leadingIconResId`를 사용하여 버튼 텍스트 앞에 아이콘을 추가할 수 있습니다. * 활성화(`enabled`) 상태에 따라 컨테이너 색상, 콘텐츠 색상, 보더 스타일이 자동으로 변경됩니다. * **미리보기 추가** * 버튼의 타입(`FILLED`, `OUTLINED`, `GHOST`), 계층, 크기, 활성화 상태, 모서리 모양 등 모든 조합을 시각적으로 확인할 수 있는 상세한 `@Preview` 코드를 추가했습니다. --- .../component/button/PrezelButton.kt | 330 ++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt new file mode 100644 index 00000000..4ba8ada2 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt @@ -0,0 +1,330 @@ +package com.team.prezel.core.designsystem.component.button + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.foundation.color.PrezelColors +import com.team.prezel.core.designsystem.foundation.number.PrezelShapes +import com.team.prezel.core.designsystem.foundation.number.PrezelSpacing +import com.team.prezel.core.designsystem.foundation.number.PrezelStroke +import com.team.prezel.core.designsystem.icon.PrezelIcons +import com.team.prezel.core.designsystem.preview.PreviewScaffold +import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.theme.PrezelColorScheme +import com.team.prezel.core.designsystem.theme.PrezelTheme +import kotlinx.collections.immutable.persistentListOf + +enum class PrezelButtonType { + FILLED, + OUTLINED, + GHOST, +} + +enum class PrezelButtonHierarchy { + PRIMARY, + SECONDARY, +} + +enum class PrezelButtonSize { + XSMALL, + SMALL, + REGULAR, +} + +@Composable +fun PrezelButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + @DrawableRes leadingIconResId: Int? = null, + enabled: Boolean = true, + isRounded: Boolean = true, + buttonType: PrezelButtonType = PrezelButtonType.FILLED, + buttonHierarchy: PrezelButtonHierarchy = PrezelButtonHierarchy.PRIMARY, + buttonSize: PrezelButtonSize = PrezelButtonSize.REGULAR, +) { + Surface( + onClick = onClick, + modifier = modifier.semantics { role = Role.Button }, + enabled = enabled, + shape = prezelButtonShape(isRounded = isRounded), + color = prezelButtonContainerColor(type = buttonType, hierarchy = buttonHierarchy, enabled = enabled), + border = prezelButtonBorderStroke(type = buttonType, hierarchy = buttonHierarchy, enabled = enabled), + interactionSource = remember { MutableInteractionSource() }, + ) { + CompositionLocalProvider( + LocalTextStyle provides prezelButtonTextStyle(buttonSize), + LocalContentColor provides prezelButtonContentColor(type = buttonType, hierarchy = buttonHierarchy, enabled = enabled), + ) { + Row( + modifier = Modifier.padding(prezelButtonContentPadding(size = buttonSize)), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + PrezelButtonIcon(iconResId = leadingIconResId, size = buttonSize) + Text(text = text) + } + } + } +} + +@Composable +private fun PrezelButtonIcon( + @DrawableRes iconResId: Int?, + size: PrezelButtonSize, + modifier: Modifier = Modifier, + contentDescription: String? = null, +) { + if (iconResId == null) return + Icon( + painter = painterResource(iconResId), + contentDescription = contentDescription, + modifier = modifier.size( + when (size) { + PrezelButtonSize.XSMALL -> 14.dp + PrezelButtonSize.SMALL -> 16.dp + PrezelButtonSize.REGULAR -> 20.dp + }, + ), + ) + Spacer( + modifier = Modifier.width(if (size == PrezelButtonSize.REGULAR) PrezelTheme.spacing.V8 else PrezelTheme.spacing.V4), + ) +} + +@Composable +private fun prezelButtonShape( + isRounded: Boolean, + shapes: PrezelShapes = PrezelTheme.shapes, +): Shape = if (isRounded) shapes.V1000 else shapes.V4 + +@Composable +private fun prezelButtonBorderStroke( + type: PrezelButtonType, + hierarchy: PrezelButtonHierarchy, + enabled: Boolean, + colors: PrezelColors = PrezelTheme.colors, + stroke: PrezelStroke = PrezelTheme.stroke, +): BorderStroke { + if (type != PrezelButtonType.OUTLINED) return BorderStroke(0.dp, Color.Transparent) + if (!enabled) return BorderStroke(width = stroke.V1, color = colors.borderDisabled) + + val borderColor = when (hierarchy) { + PrezelButtonHierarchy.PRIMARY -> colors.interactiveRegular + PrezelButtonHierarchy.SECONDARY -> colors.borderMedium + } + + return BorderStroke(width = stroke.V1, color = borderColor) +} + +@Composable +private fun prezelButtonTextStyle(size: PrezelButtonSize): TextStyle = + when (size) { + PrezelButtonSize.XSMALL -> PrezelTheme.typography.caption2Medium + PrezelButtonSize.SMALL -> PrezelTheme.typography.body3Medium + PrezelButtonSize.REGULAR -> PrezelTheme.typography.body2Bold + } + +@Composable +private fun prezelButtonContainerColor( + type: PrezelButtonType, + hierarchy: PrezelButtonHierarchy, + enabled: Boolean, + colors: PrezelColors = PrezelTheme.colors, +): Color = + when (type) { + PrezelButtonType.FILLED -> { + if (!enabled) { + colors.bgLarge + } else if (hierarchy == PrezelButtonHierarchy.PRIMARY) { + colors.interactiveRegular + } else { + colors.bgLarge + } + } + + PrezelButtonType.OUTLINED -> Color.Transparent + PrezelButtonType.GHOST -> Color.Transparent + } + +@Composable +private fun prezelButtonContentColor( + type: PrezelButtonType, + hierarchy: PrezelButtonHierarchy, + enabled: Boolean, + colors: PrezelColors = PrezelTheme.colors, +): Color { + if (!enabled) return colors.textDisabled + if (hierarchy == PrezelButtonHierarchy.SECONDARY) return colors.textMedium + + return when (type) { + PrezelButtonType.FILLED -> if (isSystemInDarkTheme()) colors.textLarge else PrezelColorScheme.Dark.textLarge + PrezelButtonType.OUTLINED -> colors.interactiveRegular + PrezelButtonType.GHOST -> colors.interactiveRegular + } +} + +@Composable +private fun prezelButtonContentPadding( + size: PrezelButtonSize, + spacing: PrezelSpacing = PrezelTheme.spacing, +): PaddingValues { + val horizontal = when (size) { + PrezelButtonSize.XSMALL -> spacing.V10 + PrezelButtonSize.SMALL -> spacing.V12 + PrezelButtonSize.REGULAR -> spacing.V16 + } + + val vertical = when (size) { + PrezelButtonSize.XSMALL -> spacing.V6 + PrezelButtonSize.SMALL -> spacing.V8 + PrezelButtonSize.REGULAR -> spacing.V12 + } + + return PaddingValues(horizontal = horizontal, vertical = vertical) +} + +@ThemePreview +@Composable +private fun PrezelButtonPreviewFilled() { + PrezelTheme { + PrezelButtonPreviewByType(type = PrezelButtonType.FILLED) + } +} + +@ThemePreview +@Composable +private fun PrezelButtonPreviewOutlined() { + PrezelTheme { + PrezelButtonPreviewByType(type = PrezelButtonType.OUTLINED) + } +} + +@ThemePreview +@Composable +private fun PrezelButtonPreviewGhost() { + PrezelTheme { + PrezelButtonPreviewByType(type = PrezelButtonType.GHOST) + } +} + +@Composable +private fun PrezelButtonPreviewByType(type: PrezelButtonType) { + val variants = persistentListOf( + true to false, + true to true, + false to true, + false to false, + ) + + PreviewScaffold { + Text(text = type.name, style = PrezelTheme.typography.title2Medium) + variants.forEach { variant -> + HorizontalDivider() + PrezelButtonVariantSection(type = type, enabled = variant.first, isRounded = variant.second) + } + } +} + +@Composable +private fun PrezelButtonVariantSection( + type: PrezelButtonType, + enabled: Boolean, + isRounded: Boolean, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Text(text = "Hierarchy: Primary | Enabled: $enabled | Radius: $isRounded", style = PrezelTheme.typography.body3Medium) + PrezelButtonPreviewHierarchyBlock( + type = type, + hierarchy = PrezelButtonHierarchy.PRIMARY, + enabled = enabled, + isRounded = isRounded, + ) + Text(text = "Hierarchy: Secondary | Enabled: $enabled | Radius: $isRounded", style = PrezelTheme.typography.body3Medium) + PrezelButtonPreviewHierarchyBlock( + type = type, + hierarchy = PrezelButtonHierarchy.SECONDARY, + enabled = enabled, + isRounded = isRounded, + ) + } +} + +@Composable +private fun PrezelButtonPreviewHierarchyBlock( + type: PrezelButtonType, + hierarchy: PrezelButtonHierarchy, + enabled: Boolean, + isRounded: Boolean, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + PrezelButton( + text = "Label", + onClick = {}, + enabled = enabled, + isRounded = isRounded, + leadingIconResId = PrezelIcons.Blank, + buttonType = type, + buttonHierarchy = hierarchy, + buttonSize = PrezelButtonSize.XSMALL, + ) + PrezelButton( + text = "Label", + onClick = {}, + enabled = enabled, + isRounded = isRounded, + leadingIconResId = PrezelIcons.Blank, + buttonType = type, + buttonHierarchy = hierarchy, + buttonSize = PrezelButtonSize.SMALL, + ) + PrezelButton( + text = "Label", + onClick = {}, + enabled = enabled, + isRounded = isRounded, + leadingIconResId = PrezelIcons.Blank, + buttonType = type, + buttonHierarchy = hierarchy, + buttonSize = PrezelButtonSize.REGULAR, + ) + } + } +} From 219021c4836d84cfeb463c958b84128661e44ad4 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 02:48:05 +0900 Subject: [PATCH 04/21] =?UTF-8?q?feat:=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EC=B6=94=EC=83=81=ED=99=94=20`Ic?= =?UTF-8?q?onSource`=20=EB=8F=84=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 아이콘 리소스를 추상화하는 `IconSource` 인터페이스를 추가하여 다양한 종류의 아이콘(Drawable 등)을 일관된 방식으로 처리할 수 있는 기반을 마련했습니다. * **`IconSource` 인터페이스 추가**: `@Composable fun painter(): Painter` 함수를 통해 Painter 리소스를 반환하도록 정의했습니다. * **`DrawableIcon` 구현체 추가**: `@DrawableRes` ID를 받아 `IconSource`를 구현하는 데이터 클래스를 추가했습니다. --- .../core/designsystem/icon/IconSource.kt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/icon/IconSource.kt diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/icon/IconSource.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/icon/IconSource.kt new file mode 100644 index 00000000..e49d80dc --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/icon/IconSource.kt @@ -0,0 +1,21 @@ +package com.team.prezel.core.designsystem.icon + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource + +@Immutable +interface IconSource { + @Composable + fun painter(): Painter +} + +@Immutable +data class DrawableIcon( + @param:DrawableRes val resId: Int, +) : IconSource { + @Composable + override fun painter(): Painter = painterResource(resId) +} From 84da40b609293703f415dfa92fa0dc20e7340d45 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 02:48:31 +0900 Subject: [PATCH 05/21] =?UTF-8?q?feat:=20`PrezelButton`=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EB=B0=8F=20=EA=B5=AC=EC=84=B1=20=EC=9A=94?= =?UTF-8?q?=EC=86=8C=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 디자인 시스템의 `PrezelButton`에 사용될 스타일 속성과 하위 구성 요소를 정의하는 `PrezelButtonStyle.kt` 파일을 추가했습니다. * **스타일 속성 정의**: * `PrezelButtonType`: `FILLED`, `OUTLINED`, `GHOST` * `PrezelButtonHierarchy`: `PRIMARY`, `SECONDARY` * `PrezelButtonSize`: `XSMALL`, `SMALL`, `REGULAR` * `PrezelButtonStyle`: 위의 속성들을 조합하여 버튼 스타일을 구성하는 데이터 클래스 * **Composable 함수 추가**: * 버튼의 모양(`prezelButtonShape`), 테두리(`prezelButtonBorderStroke`), 텍스트 스타일(`prezelButtonTextStyle`), 배경색(`prezelButtonContainerColor`), 콘텐츠 색상(`prezelButtonContentColor`), 내부 패딩(`prezelButtonContentPadding`)을 동적으로 결정하는 내부 함수들을 구현했습니다. * `PrezelButtonIcon`: 버튼 내부에 사용될 아이콘과 아이콘 좌우의 공백을 처리하는 컴포저블을 추가했습니다. --- .../component/button/PrezelButtonStyle.kt | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt new file mode 100644 index 00000000..0495074d --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt @@ -0,0 +1,160 @@ +package com.team.prezel.core.designsystem.component.button + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.foundation.color.PrezelColors +import com.team.prezel.core.designsystem.foundation.number.PrezelShapes +import com.team.prezel.core.designsystem.foundation.number.PrezelSpacing +import com.team.prezel.core.designsystem.foundation.number.PrezelStroke +import com.team.prezel.core.designsystem.icon.IconSource +import com.team.prezel.core.designsystem.theme.PrezelColorScheme +import com.team.prezel.core.designsystem.theme.PrezelTheme + +enum class PrezelButtonType { + FILLED, + OUTLINED, + GHOST, +} + +enum class PrezelButtonHierarchy { + PRIMARY, + SECONDARY, +} + +enum class PrezelButtonSize { + XSMALL, + SMALL, + REGULAR, +} + +data class PrezelButtonStyle( + val buttonType: PrezelButtonType = PrezelButtonType.FILLED, + val buttonHierarchy: PrezelButtonHierarchy = PrezelButtonHierarchy.PRIMARY, + val buttonSize: PrezelButtonSize = PrezelButtonSize.REGULAR, + val isRounded: Boolean = false, +) + +@Composable +internal fun PrezelButtonIcon( + icon: IconSource?, + size: PrezelButtonSize, + modifier: Modifier = Modifier, + contentDescription: String? = null, +) { + if (icon == null) return + Icon( + painter = icon.painter(), + contentDescription = contentDescription, + modifier = modifier.size( + when (size) { + PrezelButtonSize.XSMALL -> 14.dp + PrezelButtonSize.SMALL -> 16.dp + PrezelButtonSize.REGULAR -> 20.dp + }, + ), + ) + Spacer( + modifier = Modifier.width(if (size == PrezelButtonSize.REGULAR) PrezelTheme.spacing.V8 else PrezelTheme.spacing.V4), + ) +} + +@Composable +internal fun prezelButtonShape( + isRounded: Boolean, + shapes: PrezelShapes = PrezelTheme.shapes, +): Shape = if (isRounded) shapes.V1000 else shapes.V4 + +@Composable +internal fun prezelButtonBorderStroke( + type: PrezelButtonType, + hierarchy: PrezelButtonHierarchy, + enabled: Boolean, + colors: PrezelColors = PrezelTheme.colors, + stroke: PrezelStroke = PrezelTheme.stroke, +): BorderStroke { + if (type != PrezelButtonType.OUTLINED) return BorderStroke(0.dp, Color.Transparent) + if (!enabled) return BorderStroke(width = stroke.V1, color = colors.borderDisabled) + + val borderColor = when (hierarchy) { + PrezelButtonHierarchy.PRIMARY -> colors.interactiveRegular + PrezelButtonHierarchy.SECONDARY -> colors.borderMedium + } + + return BorderStroke(width = stroke.V1, color = borderColor) +} + +@Composable +internal fun prezelButtonTextStyle(size: PrezelButtonSize): TextStyle = + when (size) { + PrezelButtonSize.XSMALL -> PrezelTheme.typography.caption2Medium + PrezelButtonSize.SMALL -> PrezelTheme.typography.body3Medium + PrezelButtonSize.REGULAR -> PrezelTheme.typography.body2Bold + } + +@Composable +internal fun prezelButtonContainerColor( + type: PrezelButtonType, + hierarchy: PrezelButtonHierarchy, + enabled: Boolean, + colors: PrezelColors = PrezelTheme.colors, +): Color = + when (type) { + PrezelButtonType.FILLED -> { + if (!enabled || hierarchy == PrezelButtonHierarchy.SECONDARY) { + colors.bgLarge + } else { + colors.interactiveRegular + } + } + + PrezelButtonType.OUTLINED -> Color.Transparent + PrezelButtonType.GHOST -> Color.Transparent + } + +@Composable +internal fun prezelButtonContentColor( + type: PrezelButtonType, + hierarchy: PrezelButtonHierarchy, + enabled: Boolean, + colors: PrezelColors = PrezelTheme.colors, +): Color { + if (!enabled) return colors.textDisabled + if (hierarchy == PrezelButtonHierarchy.SECONDARY) return colors.textMedium + + return when (type) { + PrezelButtonType.FILLED -> if (isSystemInDarkTheme()) colors.textLarge else PrezelColorScheme.Dark.textLarge + PrezelButtonType.OUTLINED -> colors.interactiveRegular + PrezelButtonType.GHOST -> colors.interactiveRegular + } +} + +@Composable +internal fun prezelButtonContentPadding( + size: PrezelButtonSize, + spacing: PrezelSpacing = PrezelTheme.spacing, +): PaddingValues { + val horizontal = when (size) { + PrezelButtonSize.XSMALL -> spacing.V10 + PrezelButtonSize.SMALL -> spacing.V12 + PrezelButtonSize.REGULAR -> spacing.V16 + } + + val vertical = when (size) { + PrezelButtonSize.XSMALL -> spacing.V6 + PrezelButtonSize.SMALL -> spacing.V8 + PrezelButtonSize.REGULAR -> spacing.V12 + } + + return PaddingValues(horizontal = horizontal, vertical = vertical) +} From 813507b10d79cb13ccca7c6f3af389b38d3803bc Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 02:48:44 +0900 Subject: [PATCH 06/21] =?UTF-8?q?refactor:=20PrezelButton=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EC=9C=A0=EC=97=B0=EC=84=B1=20=ED=99=95?= =?UTF-8?q?=EB=B3=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelButton`의 파라미터 구조를 스타일 객체(`PrezelButtonStyle`) 기반으로 변경하여 확장성을 높이고, 아이콘 처리를 `IconSource`로 통합하여 유연성을 강화했습니다. * **구조 변경**: `buttonType`, `buttonHierarchy`, `buttonSize`, `isRounded` 등 개별 파라미터를 `PrezelButtonStyle` 데이터 클래스로 통합했습니다. * **아이콘 처리 개선**: `@DrawableRes` 정수 ID 대신 `IconSource` 타입을 사용하도록 `icon` 파라미터를 변경하여 벡터 아이콘 등 다양한 아이콘 소스를 지원하도록 개선했습니다. * **기능 추가**: 텍스트(`text`)와 아이콘(`icon`)이 모두 `null`일 경우 에러를 발생시키는 로직을 추가하여 버튼이 비어있지 않도록 보장합니다. * **코드 정리**: 버튼의 속성(색상, 모양, 패딩 등)을 결정하는 내부 `private` 함수들을 제거하고, 관련 로직을 `PrezelButtonStyle`로 이전했습니다. --- .../component/button/PrezelButton.kt | 204 +++--------------- 1 file changed, 31 insertions(+), 173 deletions(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt index 4ba8ada2..0605d0fc 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt @@ -1,19 +1,11 @@ package com.team.prezel.core.designsystem.component.button -import androidx.annotation.DrawableRes -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Surface @@ -23,54 +15,30 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.foundation.color.PrezelColors -import com.team.prezel.core.designsystem.foundation.number.PrezelShapes -import com.team.prezel.core.designsystem.foundation.number.PrezelSpacing -import com.team.prezel.core.designsystem.foundation.number.PrezelStroke +import com.team.prezel.core.designsystem.icon.DrawableIcon +import com.team.prezel.core.designsystem.icon.IconSource import com.team.prezel.core.designsystem.icon.PrezelIcons import com.team.prezel.core.designsystem.preview.PreviewScaffold import com.team.prezel.core.designsystem.preview.ThemePreview -import com.team.prezel.core.designsystem.theme.PrezelColorScheme import com.team.prezel.core.designsystem.theme.PrezelTheme import kotlinx.collections.immutable.persistentListOf -enum class PrezelButtonType { - FILLED, - OUTLINED, - GHOST, -} - -enum class PrezelButtonHierarchy { - PRIMARY, - SECONDARY, -} - -enum class PrezelButtonSize { - XSMALL, - SMALL, - REGULAR, -} - @Composable fun PrezelButton( - text: String, onClick: () -> Unit, modifier: Modifier = Modifier, - @DrawableRes leadingIconResId: Int? = null, + text: String? = null, + icon: IconSource? = null, enabled: Boolean = true, - isRounded: Boolean = true, - buttonType: PrezelButtonType = PrezelButtonType.FILLED, - buttonHierarchy: PrezelButtonHierarchy = PrezelButtonHierarchy.PRIMARY, - buttonSize: PrezelButtonSize = PrezelButtonSize.REGULAR, + style: PrezelButtonStyle = PrezelButtonStyle(), ) { + if (text == null && icon == null) error("Button must have text or icon") + val (buttonType, buttonHierarchy, buttonSize, isRounded) = style + Surface( onClick = onClick, modifier = modifier.semantics { role = Role.Button }, @@ -89,129 +57,13 @@ fun PrezelButton( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { - PrezelButtonIcon(iconResId = leadingIconResId, size = buttonSize) - Text(text = text) + PrezelButtonIcon(icon = icon, size = buttonSize) + text?.let { Text(text = it) } } } } } -@Composable -private fun PrezelButtonIcon( - @DrawableRes iconResId: Int?, - size: PrezelButtonSize, - modifier: Modifier = Modifier, - contentDescription: String? = null, -) { - if (iconResId == null) return - Icon( - painter = painterResource(iconResId), - contentDescription = contentDescription, - modifier = modifier.size( - when (size) { - PrezelButtonSize.XSMALL -> 14.dp - PrezelButtonSize.SMALL -> 16.dp - PrezelButtonSize.REGULAR -> 20.dp - }, - ), - ) - Spacer( - modifier = Modifier.width(if (size == PrezelButtonSize.REGULAR) PrezelTheme.spacing.V8 else PrezelTheme.spacing.V4), - ) -} - -@Composable -private fun prezelButtonShape( - isRounded: Boolean, - shapes: PrezelShapes = PrezelTheme.shapes, -): Shape = if (isRounded) shapes.V1000 else shapes.V4 - -@Composable -private fun prezelButtonBorderStroke( - type: PrezelButtonType, - hierarchy: PrezelButtonHierarchy, - enabled: Boolean, - colors: PrezelColors = PrezelTheme.colors, - stroke: PrezelStroke = PrezelTheme.stroke, -): BorderStroke { - if (type != PrezelButtonType.OUTLINED) return BorderStroke(0.dp, Color.Transparent) - if (!enabled) return BorderStroke(width = stroke.V1, color = colors.borderDisabled) - - val borderColor = when (hierarchy) { - PrezelButtonHierarchy.PRIMARY -> colors.interactiveRegular - PrezelButtonHierarchy.SECONDARY -> colors.borderMedium - } - - return BorderStroke(width = stroke.V1, color = borderColor) -} - -@Composable -private fun prezelButtonTextStyle(size: PrezelButtonSize): TextStyle = - when (size) { - PrezelButtonSize.XSMALL -> PrezelTheme.typography.caption2Medium - PrezelButtonSize.SMALL -> PrezelTheme.typography.body3Medium - PrezelButtonSize.REGULAR -> PrezelTheme.typography.body2Bold - } - -@Composable -private fun prezelButtonContainerColor( - type: PrezelButtonType, - hierarchy: PrezelButtonHierarchy, - enabled: Boolean, - colors: PrezelColors = PrezelTheme.colors, -): Color = - when (type) { - PrezelButtonType.FILLED -> { - if (!enabled) { - colors.bgLarge - } else if (hierarchy == PrezelButtonHierarchy.PRIMARY) { - colors.interactiveRegular - } else { - colors.bgLarge - } - } - - PrezelButtonType.OUTLINED -> Color.Transparent - PrezelButtonType.GHOST -> Color.Transparent - } - -@Composable -private fun prezelButtonContentColor( - type: PrezelButtonType, - hierarchy: PrezelButtonHierarchy, - enabled: Boolean, - colors: PrezelColors = PrezelTheme.colors, -): Color { - if (!enabled) return colors.textDisabled - if (hierarchy == PrezelButtonHierarchy.SECONDARY) return colors.textMedium - - return when (type) { - PrezelButtonType.FILLED -> if (isSystemInDarkTheme()) colors.textLarge else PrezelColorScheme.Dark.textLarge - PrezelButtonType.OUTLINED -> colors.interactiveRegular - PrezelButtonType.GHOST -> colors.interactiveRegular - } -} - -@Composable -private fun prezelButtonContentPadding( - size: PrezelButtonSize, - spacing: PrezelSpacing = PrezelTheme.spacing, -): PaddingValues { - val horizontal = when (size) { - PrezelButtonSize.XSMALL -> spacing.V10 - PrezelButtonSize.SMALL -> spacing.V12 - PrezelButtonSize.REGULAR -> spacing.V16 - } - - val vertical = when (size) { - PrezelButtonSize.XSMALL -> spacing.V6 - PrezelButtonSize.SMALL -> spacing.V8 - PrezelButtonSize.REGULAR -> spacing.V12 - } - - return PaddingValues(horizontal = horizontal, vertical = vertical) -} - @ThemePreview @Composable private fun PrezelButtonPreviewFilled() { @@ -297,33 +149,39 @@ private fun PrezelButtonPreviewHierarchyBlock( Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { PrezelButton( text = "Label", + icon = DrawableIcon(PrezelIcons.Blank), onClick = {}, enabled = enabled, - isRounded = isRounded, - leadingIconResId = PrezelIcons.Blank, - buttonType = type, - buttonHierarchy = hierarchy, - buttonSize = PrezelButtonSize.XSMALL, + style = PrezelButtonStyle( + buttonType = type, + buttonHierarchy = hierarchy, + buttonSize = PrezelButtonSize.XSMALL, + isRounded = isRounded, + ), ) PrezelButton( text = "Label", + icon = DrawableIcon(PrezelIcons.Blank), onClick = {}, enabled = enabled, - isRounded = isRounded, - leadingIconResId = PrezelIcons.Blank, - buttonType = type, - buttonHierarchy = hierarchy, - buttonSize = PrezelButtonSize.SMALL, + style = PrezelButtonStyle( + buttonType = type, + buttonHierarchy = hierarchy, + buttonSize = PrezelButtonSize.SMALL, + isRounded = isRounded, + ), ) PrezelButton( text = "Label", + icon = DrawableIcon(PrezelIcons.Blank), onClick = {}, enabled = enabled, - isRounded = isRounded, - leadingIconResId = PrezelIcons.Blank, - buttonType = type, - buttonHierarchy = hierarchy, - buttonSize = PrezelButtonSize.REGULAR, + style = PrezelButtonStyle( + buttonType = type, + buttonHierarchy = hierarchy, + buttonSize = PrezelButtonSize.REGULAR, + isRounded = isRounded, + ), ) } } From ade96704bfa5a39526df7ea0ff65c25eaea23d93 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 03:07:57 +0900 Subject: [PATCH 07/21] =?UTF-8?q?refactor:=20`PrezelButtonStyle`=EC=97=90?= =?UTF-8?q?=20`@Immutable`=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compose 컴파일러가 `PrezelButtonStyle`을 안정적인 클래스로 인식하도록 `@Immutable` 어노테이션을 추가했습니다. 이를 통해 불필요한 리컴포지션을 방지하여 성능을 최적화합니다. --- .../core/designsystem/component/button/PrezelButtonStyle.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt index 0495074d..eb4e64f2 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape @@ -38,6 +39,7 @@ enum class PrezelButtonSize { REGULAR, } +@Immutable data class PrezelButtonStyle( val buttonType: PrezelButtonType = PrezelButtonType.FILLED, val buttonHierarchy: PrezelButtonHierarchy = PrezelButtonHierarchy.PRIMARY, From 391cc38147d6b54e8749021c4c18b0e91460fc24 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 03:08:09 +0900 Subject: [PATCH 08/21] =?UTF-8?q?feat:=20=EB=B2=84=ED=8A=BC=20=ED=94=84?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=9C=A0?= =?UTF-8?q?=ED=8B=B8=EB=A6=AC=ED=8B=B0=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 디자인 시스템의 버튼 컴포넌트를 시각적으로 테스트하고 검증하기 위한 공통 프리뷰 유틸리티를 추가했습니다. 이 유틸리티는 버튼의 다양한 상태(Type, Hierarchy, Size, Enabled, Rounded) 조합을 체계적으로 확인할 수 있도록 돕습니다. * `PrezelButtonPreviewByType`: 버튼 타입(`PrezelButtonType`)에 따라 모든 변형(variant)을 표시하는 최상위 프리뷰 컨테이너 * `PrezelButtonVariantSection`, `PrezelButtonPreviewHierarchyBlock`: 활성화(enabled), 둥근 모서리(isRounded), 계층(hierarchy) 등 다양한 속성 조합에 따라 버튼을 섹션별로 그룹화하여 렌더링 * `PreviewVariants`, `PreviewSizes`: 테스트할 버튼의 상태와 크기 목록을 `immutable.persistentListOf`를 사용해 정의 --- .../component/button/PrezelButtonPreview.kt | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonPreview.kt diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonPreview.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonPreview.kt new file mode 100644 index 00000000..0f3f21e0 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonPreview.kt @@ -0,0 +1,115 @@ +package com.team.prezel.core.designsystem.component.button + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.preview.PreviewScaffold +import com.team.prezel.core.designsystem.theme.PrezelTheme +import kotlinx.collections.immutable.persistentListOf + +internal typealias PrezelButtonPreviewContent = @Composable (PrezelButtonStyle, Boolean) -> Unit + +@Immutable +private data class PreviewVariant( + val enabled: Boolean, + val isRounded: Boolean, +) + +private val PreviewVariants = persistentListOf( + PreviewVariant(enabled = true, isRounded = false), + PreviewVariant(enabled = true, isRounded = true), + PreviewVariant(enabled = false, isRounded = true), + PreviewVariant(enabled = false, isRounded = false), +) + +private val PreviewSizes = persistentListOf( + PrezelButtonSize.XSMALL, + PrezelButtonSize.SMALL, + PrezelButtonSize.REGULAR, +) + +@Composable +internal fun PrezelButtonPreviewByType( + type: PrezelButtonType, + content: PrezelButtonPreviewContent, +) { + PreviewScaffold { + Text(text = type.name, style = PrezelTheme.typography.title2Medium) + + PreviewVariants.forEach { variant -> + HorizontalDivider() + PrezelButtonVariantSection( + type = type, + enabled = variant.enabled, + isRounded = variant.isRounded, + content = content, + ) + } + } +} + +@Composable +private fun PrezelButtonVariantSection( + type: PrezelButtonType, + enabled: Boolean, + isRounded: Boolean, + content: PrezelButtonPreviewContent, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Text(text = "Hierarchy: Primary | Enabled: $enabled | Radius: $isRounded", style = PrezelTheme.typography.body3Medium) + PrezelButtonPreviewHierarchyBlock( + type = type, + hierarchy = PrezelButtonHierarchy.PRIMARY, + enabled = enabled, + isRounded = isRounded, + content = content, + ) + Text(text = "Hierarchy: Secondary | Enabled: $enabled | Radius: $isRounded", style = PrezelTheme.typography.body3Medium) + PrezelButtonPreviewHierarchyBlock( + type = type, + hierarchy = PrezelButtonHierarchy.SECONDARY, + enabled = enabled, + isRounded = isRounded, + content = content, + ) + } +} + +@Composable +private fun PrezelButtonPreviewHierarchyBlock( + type: PrezelButtonType, + hierarchy: PrezelButtonHierarchy, + enabled: Boolean, + isRounded: Boolean, + content: PrezelButtonPreviewContent, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + PreviewSizes.forEach { size -> + content( + PrezelButtonStyle( + buttonType = type, + buttonHierarchy = hierarchy, + buttonSize = size, + isRounded = isRounded, + ), + enabled, + ) + } + } + } +} From b2b10cb5f11ec1a14df7468d87ce85ee0e19adc1 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 03:08:30 +0900 Subject: [PATCH 09/21] =?UTF-8?q?refactor:=20`PrezelButton`=20=EB=AF=B8?= =?UTF-8?q?=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EC=BD=94=EB=93=9C=20=EB=8B=A8?= =?UTF-8?q?=EC=88=9C=ED=99=94=20=EB=B0=8F=20=EC=9E=AC=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelButton` 컴포넌트의 미리보기(@Preview) 구현을 단순화하고 재사용성을 높였습니다. * **리팩토링**: `PrezelButtonPreviewByType` 함수의 역할을 분리하여, 레이아웃(`PrezelButtonPreviewByType`)과 아이템 렌더링(`PrezelButtonPreviewItem`)으로 구조를 개선했습니다. * **단순화**: 복잡한 조합(활성화, 둥근 모서리 등)을 생성하던 `PrezelButtonVariantSection`과 `PrezelButtonPreviewHierarchyBlock` 함수를 제거하고, `PrezelButtonPreviewItem`을 사용하여 각 버튼 상태를 직접 명확하게 보여주도록 변경했습니다. * **기타**: 불필요한 import 문과 `persistentListOf` 사용 코드를 정리했습니다. --- .../component/button/PrezelButton.kt | 114 ++---------------- 1 file changed, 12 insertions(+), 102 deletions(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt index 0605d0fc..489affc5 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt @@ -2,10 +2,8 @@ package com.team.prezel.core.designsystem.component.button import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Surface @@ -18,14 +16,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.icon.DrawableIcon import com.team.prezel.core.designsystem.icon.IconSource import com.team.prezel.core.designsystem.icon.PrezelIcons -import com.team.prezel.core.designsystem.preview.PreviewScaffold import com.team.prezel.core.designsystem.preview.ThemePreview import com.team.prezel.core.designsystem.theme.PrezelTheme -import kotlinx.collections.immutable.persistentListOf @Composable fun PrezelButton( @@ -68,7 +63,7 @@ fun PrezelButton( @Composable private fun PrezelButtonPreviewFilled() { PrezelTheme { - PrezelButtonPreviewByType(type = PrezelButtonType.FILLED) + PrezelButtonPreviewByType(type = PrezelButtonType.FILLED, content = ::PrezelButtonPreviewItem) } } @@ -76,7 +71,7 @@ private fun PrezelButtonPreviewFilled() { @Composable private fun PrezelButtonPreviewOutlined() { PrezelTheme { - PrezelButtonPreviewByType(type = PrezelButtonType.OUTLINED) + PrezelButtonPreviewByType(type = PrezelButtonType.OUTLINED, content = ::PrezelButtonPreviewItem) } } @@ -84,105 +79,20 @@ private fun PrezelButtonPreviewOutlined() { @Composable private fun PrezelButtonPreviewGhost() { PrezelTheme { - PrezelButtonPreviewByType(type = PrezelButtonType.GHOST) + PrezelButtonPreviewByType(type = PrezelButtonType.GHOST, content = ::PrezelButtonPreviewItem) } } @Composable -private fun PrezelButtonPreviewByType(type: PrezelButtonType) { - val variants = persistentListOf( - true to false, - true to true, - false to true, - false to false, - ) - - PreviewScaffold { - Text(text = type.name, style = PrezelTheme.typography.title2Medium) - variants.forEach { variant -> - HorizontalDivider() - PrezelButtonVariantSection(type = type, enabled = variant.first, isRounded = variant.second) - } - } -} - -@Composable -private fun PrezelButtonVariantSection( - type: PrezelButtonType, +private fun PrezelButtonPreviewItem( + style: PrezelButtonStyle, enabled: Boolean, - isRounded: Boolean, - modifier: Modifier = Modifier, ) { - Column( - modifier = modifier, - verticalArrangement = Arrangement.spacedBy(12.dp), - ) { - Text(text = "Hierarchy: Primary | Enabled: $enabled | Radius: $isRounded", style = PrezelTheme.typography.body3Medium) - PrezelButtonPreviewHierarchyBlock( - type = type, - hierarchy = PrezelButtonHierarchy.PRIMARY, - enabled = enabled, - isRounded = isRounded, - ) - Text(text = "Hierarchy: Secondary | Enabled: $enabled | Radius: $isRounded", style = PrezelTheme.typography.body3Medium) - PrezelButtonPreviewHierarchyBlock( - type = type, - hierarchy = PrezelButtonHierarchy.SECONDARY, - enabled = enabled, - isRounded = isRounded, - ) - } -} - -@Composable -private fun PrezelButtonPreviewHierarchyBlock( - type: PrezelButtonType, - hierarchy: PrezelButtonHierarchy, - enabled: Boolean, - isRounded: Boolean, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier, - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - PrezelButton( - text = "Label", - icon = DrawableIcon(PrezelIcons.Blank), - onClick = {}, - enabled = enabled, - style = PrezelButtonStyle( - buttonType = type, - buttonHierarchy = hierarchy, - buttonSize = PrezelButtonSize.XSMALL, - isRounded = isRounded, - ), - ) - PrezelButton( - text = "Label", - icon = DrawableIcon(PrezelIcons.Blank), - onClick = {}, - enabled = enabled, - style = PrezelButtonStyle( - buttonType = type, - buttonHierarchy = hierarchy, - buttonSize = PrezelButtonSize.SMALL, - isRounded = isRounded, - ), - ) - PrezelButton( - text = "Label", - icon = DrawableIcon(PrezelIcons.Blank), - onClick = {}, - enabled = enabled, - style = PrezelButtonStyle( - buttonType = type, - buttonHierarchy = hierarchy, - buttonSize = PrezelButtonSize.REGULAR, - isRounded = isRounded, - ), - ) - } - } + PrezelButton( + text = "Label", + icon = DrawableIcon(PrezelIcons.Blank), + onClick = {}, + enabled = enabled, + style = style, + ) } From 0a3370c2166374c7fa98c43c5a3b2630ed173f4f Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 03:08:37 +0900 Subject: [PATCH 10/21] =?UTF-8?q?feat:=20PrezelTextButton=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 텍스트만으로 구성된 공통 버튼 컴포넌트인 `PrezelTextButton`을 추가했습니다. * **컴포넌트 구현**: `PrezelButton`을 재사용하여 텍스트 기반의 버튼을 생성합니다. * **파라미터**: `text`, `onClick`, `modifier`, `enabled`, `style`을 설정할 수 있습니다. * **미리보기 추가**: Filled, Outlined, Ghost 타입별로 `PrezelTextButton`의 상태(활성/비활성)를 확인할 수 있는 Preview 코드를 작성했습니다. --- .../component/button/PrezelTextButton.kt | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelTextButton.kt diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelTextButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelTextButton.kt new file mode 100644 index 00000000..3973f499 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelTextButton.kt @@ -0,0 +1,60 @@ +package com.team.prezel.core.designsystem.component.button + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.theme.PrezelTheme + +@Composable +fun PrezelTextButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + style: PrezelButtonStyle = PrezelButtonStyle(), +) { + PrezelButton( + text = text, + onClick = onClick, + modifier = modifier, + enabled = enabled, + style = style, + ) +} + +@ThemePreview +@Composable +private fun PrezelTextButtonPreviewFilled() { + PrezelTheme { + PrezelButtonPreviewByType(type = PrezelButtonType.FILLED, content = ::PrezelTextButtonPreviewItem) + } +} + +@ThemePreview +@Composable +private fun PrezelTextButtonPreviewOutlined() { + PrezelTheme { + PrezelButtonPreviewByType(type = PrezelButtonType.OUTLINED, content = ::PrezelTextButtonPreviewItem) + } +} + +@ThemePreview +@Composable +private fun PrezelTextButtonPreviewGhost() { + PrezelTheme { + PrezelButtonPreviewByType(type = PrezelButtonType.GHOST, content = ::PrezelTextButtonPreviewItem) + } +} + +@Composable +private fun PrezelTextButtonPreviewItem( + style: PrezelButtonStyle, + enabled: Boolean, +) { + PrezelTextButton( + text = "Label", + onClick = {}, + enabled = enabled, + style = style, + ) +} From 03b3194d74f6be63758f9a8709eac9bc7aa8c8ed Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 03:18:51 +0900 Subject: [PATCH 11/21] =?UTF-8?q?refactor:=20`PrezelButton`=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EB=B2=84=ED=8A=BC=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EB=B0=8F=20=ED=8C=A8=EB=94=A9=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelButton`에서 아이콘만 포함된 버튼의 패딩 계산 방식을 개선하고, 아이콘 컴포넌트 내 불필요한 `Spacer`를 제거하여 코드를 최적화했습니다. * **리팩토링**: `prezelButtonContentPadding` 함수에 `onlyIcon` 파라미터를 추가하여, 텍스트 없이 아이콘만 있을 경우를 처리하는 `prezelIconButtonContentPadding` 함수를 호출하도록 로직을 분리했습니다. * **기능 추가**: 아이콘 버튼의 크기(`XSMALL`, `SMALL`, `REGULAR`)에 따라 적절한 패딩 값을 반환하는 `prezelIconButtonContentPadding` 함수를 추가했습니다. * **코드 개선**: `LeadingIcon` 컴포저블 내에서 아이콘과 텍스트 사이의 간격을 조절하던 `Spacer`를 제거했습니다. 이는 버튼 자체의 `contentPadding`으로 간격을 관리하는 것이 더 적절하기 때문입니다. --- .../component/button/PrezelButtonStyle.kt | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt index eb4e64f2..0ec7a5f6 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt @@ -3,9 +3,7 @@ package com.team.prezel.core.designsystem.component.button import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable @@ -66,9 +64,6 @@ internal fun PrezelButtonIcon( }, ), ) - Spacer( - modifier = Modifier.width(if (size == PrezelButtonSize.REGULAR) PrezelTheme.spacing.V8 else PrezelTheme.spacing.V4), - ) } @Composable @@ -144,8 +139,11 @@ internal fun prezelButtonContentColor( @Composable internal fun prezelButtonContentPadding( size: PrezelButtonSize, + onlyIcon: Boolean = false, spacing: PrezelSpacing = PrezelTheme.spacing, ): PaddingValues { + if (onlyIcon) return prezelIconButtonContentPadding(size) + val horizontal = when (size) { PrezelButtonSize.XSMALL -> spacing.V10 PrezelButtonSize.SMALL -> spacing.V12 @@ -160,3 +158,14 @@ internal fun prezelButtonContentPadding( return PaddingValues(horizontal = horizontal, vertical = vertical) } + +@Composable +private fun prezelIconButtonContentPadding( + size: PrezelButtonSize, + spacing: PrezelSpacing = PrezelTheme.spacing, +): PaddingValues = + when (size) { + PrezelButtonSize.XSMALL -> spacing.V8 + PrezelButtonSize.SMALL -> spacing.V10 + PrezelButtonSize.REGULAR -> spacing.V14 + }.let { spacing -> PaddingValues(all = spacing) } From ba49636f1f645f3902be473d0bd018b07f14ca49 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 03:19:11 +0900 Subject: [PATCH 12/21] =?UTF-8?q?refactor:=20`PrezelButton`=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98-=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EA=B0=84=20?= =?UTF-8?q?=EA=B0=84=EA=B2=A9=20=EB=B0=8F=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EC=A0=84=EC=9A=A9=20=ED=8C=A8=EB=94=A9=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelButton` 컴포넌트의 내부 로직을 개선하여 아이콘과 텍스트 사이의 간격을 추가하고, 아이콘만 있을 경우의 패딩을 조정했습니다. * **기능 변경**: 아이콘과 텍스트 사이에 `Spacer`를 추가하여 버튼 크기(`REGULAR` 또는 그 외)에 따라 간격을 동적으로 조절합니다. * **리팩토링**: `prezelButtonContentPadding` 함수에 `onlyIcon` 파라미터를 추가하여, 텍스트 없이 아이콘만 있는 버튼일 때 좌우 패딩이 적용되지 않도록 수정했습니다. --- .../core/designsystem/component/button/PrezelButton.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt index 489affc5..efce8c66 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt @@ -3,7 +3,9 @@ package com.team.prezel.core.designsystem.component.button import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Surface @@ -48,12 +50,15 @@ fun PrezelButton( LocalContentColor provides prezelButtonContentColor(type = buttonType, hierarchy = buttonHierarchy, enabled = enabled), ) { Row( - modifier = Modifier.padding(prezelButtonContentPadding(size = buttonSize)), + modifier = Modifier.padding(prezelButtonContentPadding(size = buttonSize, onlyIcon = text == null && icon != null)), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { PrezelButtonIcon(icon = icon, size = buttonSize) - text?.let { Text(text = it) } + text?.let { + Spacer(modifier = Modifier.width(if (buttonSize == PrezelButtonSize.REGULAR) PrezelTheme.spacing.V8 else PrezelTheme.spacing.V4)) + Text(text = it) + } } } } From fa702ae25cee171cabb7025c4ebddc7ef9b8a3f5 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 03:19:21 +0900 Subject: [PATCH 13/21] =?UTF-8?q?feat:=20`PrezelIconButton`=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 아이콘 전용 버튼인 `PrezelIconButton` 컴포저블을 추가했습니다. 이 컴포넌트는 내부적으로 `PrezelButton`을 사용하여 구현되었습니다. * **기능**: `DrawableIcon`, `onClick`, `enabled`, `style` 등의 파라미터를 통해 아이콘 버튼을 생성할 수 있습니다. * **미리보기**: Filled, Outlined, Ghost 스타일에 대한 `PrezelIconButtonPreview`를 추가하여 각 버튼 유형별 상태(활성화/비활성화)를 시각적으로 확인할 수 있도록 했습니다. --- .../component/button/PrezelIconButton.kt | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelIconButton.kt diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelIconButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelIconButton.kt new file mode 100644 index 00000000..eaa46ccb --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelIconButton.kt @@ -0,0 +1,62 @@ +package com.team.prezel.core.designsystem.component.button + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.team.prezel.core.designsystem.icon.DrawableIcon +import com.team.prezel.core.designsystem.icon.PrezelIcons +import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.theme.PrezelTheme + +@Composable +fun PrezelIconButton( + icon: DrawableIcon, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + style: PrezelButtonStyle = PrezelButtonStyle(), +) { + PrezelButton( + icon = icon, + onClick = onClick, + modifier = modifier, + enabled = enabled, + style = style, + ) +} + +@ThemePreview +@Composable +private fun PrezelIconButtonPreviewFilled() { + PrezelTheme { + PrezelButtonPreviewByType(type = PrezelButtonType.FILLED, content = ::PrezelIconButtonPreviewItem) + } +} + +@ThemePreview +@Composable +private fun PrezelIconButtonPreviewOutlined() { + PrezelTheme { + PrezelButtonPreviewByType(type = PrezelButtonType.OUTLINED, content = ::PrezelIconButtonPreviewItem) + } +} + +@ThemePreview +@Composable +private fun PrezelIconButtonPreviewGhost() { + PrezelTheme { + PrezelButtonPreviewByType(type = PrezelButtonType.GHOST, content = ::PrezelIconButtonPreviewItem) + } +} + +@Composable +private fun PrezelIconButtonPreviewItem( + style: PrezelButtonStyle, + enabled: Boolean, +) { + PrezelIconButton( + icon = DrawableIcon(resId = PrezelIcons.Blank), + onClick = {}, + enabled = enabled, + style = style, + ) +} From 3d429d6aa01741f38f004838a246463e1e8cf420 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 03:21:47 +0900 Subject: [PATCH 14/21] =?UTF-8?q?style:=20Detekt=20`DestructuringDeclarati?= =?UTF-8?q?onWithTooManyEntries`=20=EA=B7=9C=EC=B9=99=20=EB=B9=84=ED=99=9C?= =?UTF-8?q?=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit detekt-config.yml 파일에 `DestructuringDeclarationWithTooManyEntries` 규칙을 비활성화(`active: false`)하는 설정을 추가했습니다. 이를 통해 여러 항목을 가진 데이터 클래스 등을 구조 분해 할당할 때 발생하는 경고를 무시하도록 합니다. --- Prezel/detekt-config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Prezel/detekt-config.yml b/Prezel/detekt-config.yml index ec6a92e8..b6b41be9 100644 --- a/Prezel/detekt-config.yml +++ b/Prezel/detekt-config.yml @@ -26,6 +26,8 @@ style: - ThemePreview WildcardImport: active: true + DestructuringDeclarationWithTooManyEntries: + active: false complexity: active: true From 012053f0f910533bf6d4dab28f1d2626d47e9d2d Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 03:41:33 +0900 Subject: [PATCH 15/21] =?UTF-8?q?refactor:=20`PrezelButton`=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelButton` 컴포넌트의 내부 로직을 개선하여 코드 가독성과 안정성을 높였습니다. * **변경 사항**: * `text`와 `icon`의 null 여부를 `hasText`, `hasIcon` Boolean 변수로 관리하여 조건문을 간소화했습니다. * 기존의 `error` 호출을 `require`로 변경하여, 버튼에 `text`나 `icon`이 없는 경우 더 명확한 예외 메시지("Button은 text 또는 icon 중 하나는 반드시 필요합니다.")를 제공하도록 수정했습니다. * 아이콘과 텍스트가 함께 있을 때 간격(`Spacer`)을 추가하는 로직과 텍스트를 표시하는 로직의 흐름을 개선했습니다. --- .../component/button/PrezelButton.kt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt index efce8c66..4a7a46d7 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt @@ -33,7 +33,9 @@ fun PrezelButton( enabled: Boolean = true, style: PrezelButtonStyle = PrezelButtonStyle(), ) { - if (text == null && icon == null) error("Button must have text or icon") + val hasText = text != null + val hasIcon = icon != null + require(hasText || hasIcon) { "Button은 text 또는 icon 중 하나는 반드시 필요합니다." } val (buttonType, buttonHierarchy, buttonSize, isRounded) = style Surface( @@ -50,15 +52,19 @@ fun PrezelButton( LocalContentColor provides prezelButtonContentColor(type = buttonType, hierarchy = buttonHierarchy, enabled = enabled), ) { Row( - modifier = Modifier.padding(prezelButtonContentPadding(size = buttonSize, onlyIcon = text == null && icon != null)), + modifier = Modifier.padding(prezelButtonContentPadding(size = buttonSize, onlyIcon = hasIcon && !hasText)), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { PrezelButtonIcon(icon = icon, size = buttonSize) - text?.let { - Spacer(modifier = Modifier.width(if (buttonSize == PrezelButtonSize.REGULAR) PrezelTheme.spacing.V8 else PrezelTheme.spacing.V4)) - Text(text = it) + + if (!hasText) return@Row + if (hasIcon) { + val spacing = if (buttonSize == PrezelButtonSize.REGULAR) PrezelTheme.spacing.V8 else PrezelTheme.spacing.V4 + Spacer(modifier = Modifier.width(width = spacing)) } + + Text(text = text) } } } From 9cb56010d6b5b7ca0139d403d151c869ef3d4457 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 04:11:00 +0900 Subject: [PATCH 16/21] =?UTF-8?q?refactor:=20`PrezelIconButton`=EC=9D=98?= =?UTF-8?q?=20=EC=95=84=EC=9D=B4=EC=BD=98=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=20=ED=83=80=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelIconButton` 컴포저블의 아이콘 파라미터 타입을 `DrawableIcon`에서 `IconSource`로 변경했습니다. 이를 통해 아이콘 소스를 더 유연하게 처리할 수 있도록 개선했습니다. --- .../core/designsystem/component/button/PrezelIconButton.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelIconButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelIconButton.kt index eaa46ccb..740fa8e6 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelIconButton.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelIconButton.kt @@ -3,13 +3,14 @@ package com.team.prezel.core.designsystem.component.button import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.team.prezel.core.designsystem.icon.DrawableIcon +import com.team.prezel.core.designsystem.icon.IconSource import com.team.prezel.core.designsystem.icon.PrezelIcons import com.team.prezel.core.designsystem.preview.ThemePreview import com.team.prezel.core.designsystem.theme.PrezelTheme @Composable fun PrezelIconButton( - icon: DrawableIcon, + icon: IconSource, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, From 8ecbd7591ca7aa90f807bb662d980e2fe52be9be Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 04:12:15 +0900 Subject: [PATCH 17/21] =?UTF-8?q?refactor:=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=ED=94=84=EB=A6=AC=EB=B7=B0=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelButtonPreview.kt` 내 `PrezelButtonPreviewContentRow` 컴포저블의 레이아웃을 `Column`에서 `Row`로 변경하여 불필요한 중첩 구조를 제거하고 코드를 간소화했습니다. --- .../component/button/PrezelButtonPreview.kt | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonPreview.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonPreview.kt index 0f3f21e0..0694e425 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonPreview.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonPreview.kt @@ -94,22 +94,20 @@ private fun PrezelButtonPreviewHierarchyBlock( content: PrezelButtonPreviewContent, modifier: Modifier = Modifier, ) { - Column( + Row( modifier = modifier, - verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), ) { - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - PreviewSizes.forEach { size -> - content( - PrezelButtonStyle( - buttonType = type, - buttonHierarchy = hierarchy, - buttonSize = size, - isRounded = isRounded, - ), - enabled, - ) - } + PreviewSizes.forEach { size -> + content( + PrezelButtonStyle( + buttonType = type, + buttonHierarchy = hierarchy, + buttonSize = size, + isRounded = isRounded, + ), + enabled, + ) } } } From fa429c4c64b1d24e5b0fc05d08f8b4973e20f95e Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 04:19:07 +0900 Subject: [PATCH 18/21] =?UTF-8?q?refactor:=20IconSource=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=EC=97=90=20contentDescript?= =?UTF-8?q?ion=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `IconSource` 인터페이스와 이를 구현하는 `DrawableIcon` 데이터 클래스에 `contentDescription` 속성을 추가하여 아이콘의 접근성을 향상시켰습니다. * `IconSource` 인터페이스에 `contentDescription: String?` 프로퍼티 추가 * `DrawableIcon` 데이터 클래스 생성자에 `contentDescription` 파라미터 추가 및 인터페이스 구현 업데이트 --- .../prezel/core/designsystem/component/button/PrezelButton.kt | 2 +- .../core/designsystem/component/button/PrezelButtonStyle.kt | 3 +-- .../java/com/team/prezel/core/designsystem/icon/IconSource.kt | 3 +++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt index 4a7a46d7..b5a1901f 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt @@ -101,7 +101,7 @@ private fun PrezelButtonPreviewItem( ) { PrezelButton( text = "Label", - icon = DrawableIcon(PrezelIcons.Blank), + icon = DrawableIcon(resId = PrezelIcons.Blank), onClick = {}, enabled = enabled, style = style, diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt index 0ec7a5f6..f98193ed 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt @@ -50,12 +50,11 @@ internal fun PrezelButtonIcon( icon: IconSource?, size: PrezelButtonSize, modifier: Modifier = Modifier, - contentDescription: String? = null, ) { if (icon == null) return Icon( painter = icon.painter(), - contentDescription = contentDescription, + contentDescription = icon.contentDescription, modifier = modifier.size( when (size) { PrezelButtonSize.XSMALL -> 14.dp diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/icon/IconSource.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/icon/IconSource.kt index e49d80dc..40d8ecf9 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/icon/IconSource.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/icon/IconSource.kt @@ -8,12 +8,15 @@ import androidx.compose.ui.res.painterResource @Immutable interface IconSource { + val contentDescription: String? + @Composable fun painter(): Painter } @Immutable data class DrawableIcon( + override val contentDescription: String? = null, @param:DrawableRes val resId: Int, ) : IconSource { @Composable From 4ecb9c421b23765ada8c2706a1e0bf6e48d03377 Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 04:21:49 +0900 Subject: [PATCH 19/21] =?UTF-8?q?style:=20detekt-config.yml=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=B7=ED=8C=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `detekt-config.yml` 파일의 가독성 향상을 위해 전체적으로 일관된 들여쓰기를 적용했습니다. 기능적인 설정 변경은 없습니다. --- Prezel/detekt-config.yml | 88 ++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/Prezel/detekt-config.yml b/Prezel/detekt-config.yml index b6b41be9..3f97a7a1 100644 --- a/Prezel/detekt-config.yml +++ b/Prezel/detekt-config.yml @@ -1,62 +1,62 @@ build: - maxIssues: 0 + maxIssues: 0 config: - validation: true - warningsAsErrors: false - checkExhaustiveness: false + validation: true + warningsAsErrors: false + checkExhaustiveness: false processors: - active: true + active: true style: - active: true - MaxLineLength: - active: true - maxLineLength: 150 - MagicNumber: - active: false - ReturnCount: - active: true - max: 3 - UnusedPrivateMember: active: true - ignoreAnnotated: - - Preview - - ThemePreview - WildcardImport: - active: true - DestructuringDeclarationWithTooManyEntries: - active: false + MaxLineLength: + active: true + maxLineLength: 150 + MagicNumber: + active: false + ReturnCount: + active: true + max: 3 + UnusedPrivateMember: + active: true + ignoreAnnotated: + - Preview + - ThemePreview + WildcardImport: + active: true + DestructuringDeclarationWithTooManyEntries: + active: false complexity: - active: true - LongMethod: - active: true - threshold: 40 - LongParameterList: active: true - functionThreshold: 7 - constructorThreshold: 8 - CyclomaticComplexMethod: - active: true - threshold: 15 - NestedBlockDepth: - active: true - threshold: 4 + LongMethod: + active: true + threshold: 40 + LongParameterList: + active: true + functionThreshold: 7 + constructorThreshold: 8 + CyclomaticComplexMethod: + active: true + threshold: 15 + NestedBlockDepth: + active: true + threshold: 4 naming: - active: true - FunctionNaming: active: true - ignoreAnnotated: - - Composable - - Preview + FunctionNaming: + active: true + ignoreAnnotated: + - Composable + - Preview potential-bugs: - active: true + active: true exceptions: - active: true - TooGenericExceptionCaught: - active: false + active: true + TooGenericExceptionCaught: + active: false From dffe88099292eb2d33618eedea6145261893c65e Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 04:41:50 +0900 Subject: [PATCH 20/21] =?UTF-8?q?refactor:=20`PrezelTopAppBar`=20=ED=94=84?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EC=BD=94=EB=93=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelTopAppBar` 컴포넌트의 프리뷰(Preview) 코드를 개선하여 가독성을 높이고 중복을 제거했습니다. * `Surface`를 추가하여 프리뷰 배경색(`bgRegular`)을 명시적으로 지정했습니다. * 타이틀(`Text`)에 적용되던 `PrezelTextStyles.Body2Bold`를 제거했습니다. (`ProvideTextStyle`을 통해 기본 스타일이 이미 적용되고 있음) * 아이콘(`Icon`)에 직접 지정하던 `tint`를 제거하여 `LocalContentColor`를 통해 색상이 적용되도록 수정했습니다. * 스크롤 테스트 프리뷰(`PrezelTopAppBarScrollTestPreview`)의 배경색을 설정하고 `LazyColumn` 내부 아이템 레이아웃을 간소화했습니다. --- .../core/designsystem/component/TopAppBar.kt | 117 ++++++++---------- 1 file changed, 51 insertions(+), 66 deletions(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/TopAppBar.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/TopAppBar.kt index 83be6342..d14a7be9 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/TopAppBar.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/TopAppBar.kt @@ -1,5 +1,6 @@ package com.team.prezel.core.designsystem.component +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -8,6 +9,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults @@ -19,7 +21,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.foundation.typography.PrezelTextStyles import com.team.prezel.core.designsystem.icon.PrezelIcons import com.team.prezel.core.designsystem.preview.ThemePreview import com.team.prezel.core.designsystem.theme.PrezelTheme @@ -63,14 +64,9 @@ private fun prezelTopAppBarColors() = @Composable private fun PrezelTopAppBarTitleOnlyPreview() { PrezelTheme { - PrezelTopAppBar( - title = { - Text( - text = "제목", - style = PrezelTextStyles.Body2Bold.toTextStyle(), - ) - }, - ) + Surface(color = PrezelTheme.colors.bgRegular) { + PrezelTopAppBar(title = { Text(text = "제목") }) + } } } @@ -79,23 +75,19 @@ private fun PrezelTopAppBarTitleOnlyPreview() { @Composable private fun PrezelTopAppBarWithLeadingPreview() { PrezelTheme { - PrezelTopAppBar( - title = { - Text( - text = "제목", - style = PrezelTextStyles.Body2Bold.toTextStyle(), - ) - }, - leadingIcon = { - IconButton(onClick = {}) { - Icon( - painter = painterResource(PrezelIcons.Blank), - contentDescription = "뒤로가기", - tint = PrezelTheme.colors.iconRegular, - ) - } - }, - ) + Surface(color = PrezelTheme.colors.bgRegular) { + PrezelTopAppBar( + title = { Text(text = "제목") }, + leadingIcon = { + IconButton(onClick = {}) { + Icon( + painter = painterResource(PrezelIcons.Blank), + contentDescription = "뒤로가기", + ) + } + }, + ) + } } } @@ -104,39 +96,33 @@ private fun PrezelTopAppBarWithLeadingPreview() { @Composable private fun PrezelTopAppBarWithAllIconsPreview() { PrezelTheme { - PrezelTopAppBar( - title = { - Text( - text = "제목", - style = PrezelTextStyles.Body2Bold.toTextStyle(), - ) - }, - leadingIcon = { - IconButton(onClick = {}) { - Icon( - painter = painterResource(PrezelIcons.Blank), - contentDescription = "뒤로가기", - tint = PrezelTheme.colors.iconRegular, - ) - } - }, - trailingIcons = { - IconButton(onClick = {}) { - Icon( - painter = painterResource(PrezelIcons.Blank), - contentDescription = "검색", - tint = PrezelTheme.colors.iconRegular, - ) - } - IconButton(onClick = {}) { - Icon( - painter = painterResource(PrezelIcons.Blank), - contentDescription = "더보기", - tint = PrezelTheme.colors.iconRegular, - ) - } - }, - ) + Surface(color = PrezelTheme.colors.bgRegular) { + PrezelTopAppBar( + title = { Text(text = "제목") }, + leadingIcon = { + IconButton(onClick = {}) { + Icon( + painter = painterResource(PrezelIcons.Blank), + contentDescription = "뒤로가기", + ) + } + }, + trailingIcons = { + IconButton(onClick = {}) { + Icon( + painter = painterResource(PrezelIcons.Blank), + contentDescription = "검색", + ) + } + IconButton(onClick = {}) { + Icon( + painter = painterResource(PrezelIcons.Blank), + contentDescription = "더보기", + ) + } + }, + ) + } } } @@ -149,6 +135,7 @@ private fun PrezelTopAppBarScrollTestPreview() { Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + containerColor = PrezelTheme.colors.bgRegular, topBar = { PrezelTopAppBar( title = { Text("제목") }, @@ -165,14 +152,12 @@ private fun PrezelTopAppBarScrollTestPreview() { }, ) { innerPadding -> LazyColumn( - modifier = Modifier.padding(innerPadding), + modifier = Modifier + .padding(innerPadding) + .padding(horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), ) { - items(30) { index -> - Text( - text = "Item $index", - modifier = Modifier.padding(16.dp), - ) - } + items(30) { index -> Text(text = "Item $index") } } } } From 11d093d750e69a616e079295ea07763d599f3dae Mon Sep 17 00:00:00 2001 From: moondev03 Date: Fri, 23 Jan 2026 23:55:38 +0900 Subject: [PATCH 21/21] =?UTF-8?q?refactor:=20PrezelButton=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20InteractionS?= =?UTF-8?q?ource=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PrezelButton` 컴포저블에서 기본 동작에 영향을 주지 않는 불필요한 `MutableInteractionSource` 초기화 코드를 제거하여 코드를 간소화했습니다. --- .../prezel/core/designsystem/component/button/PrezelButton.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt index b5a1901f..8dba7950 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt @@ -1,6 +1,5 @@ package com.team.prezel.core.designsystem.component.button -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -12,7 +11,6 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.Role @@ -45,7 +43,6 @@ fun PrezelButton( shape = prezelButtonShape(isRounded = isRounded), color = prezelButtonContainerColor(type = buttonType, hierarchy = buttonHierarchy, enabled = enabled), border = prezelButtonBorderStroke(type = buttonType, hierarchy = buttonHierarchy, enabled = enabled), - interactionSource = remember { MutableInteractionSource() }, ) { CompositionLocalProvider( LocalTextStyle provides prezelButtonTextStyle(buttonSize),