diff --git a/Prezel/build.gradle.kts b/Prezel/build.gradle.kts index 85a41491..80ba2255 100644 --- a/Prezel/build.gradle.kts +++ b/Prezel/build.gradle.kts @@ -1,3 +1,5 @@ +import io.gitlab.arturbosch.detekt.Detekt + plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false @@ -29,7 +31,9 @@ subprojects { parallel = true } - tasks.withType().configureEach { + tasks.withType().configureEach { + jvmTarget = "21" + reports { txt.required.set(true) html.required.set(false) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChip.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChip.kt index a0478518..3d1db98f 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChip.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChip.kt @@ -1,23 +1,18 @@ package com.team.prezel.core.designsystem.component.chip import androidx.annotation.DrawableRes -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.size -import androidx.compose.foundation.layout.width -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.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipDefault +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipDefaults +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipFeedback +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipInteraction +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipLayout +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipSize +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipType import com.team.prezel.core.designsystem.icon.PrezelIcons import com.team.prezel.core.designsystem.preview.BasicPreview import com.team.prezel.core.designsystem.preview.PreviewRow @@ -27,201 +22,146 @@ import com.team.prezel.core.designsystem.theme.PrezelTheme @Composable fun PrezelChip( + text: String, modifier: Modifier = Modifier, - text: String? = null, @DrawableRes iconResId: Int? = null, - style: PrezelChipStyle = PrezelChipStyle(), + type: PrezelChipType = PrezelChipType.FILLED, + size: PrezelChipSize = PrezelChipSize.REGULAR, + interaction: PrezelChipInteraction = PrezelChipInteraction.DEFAULT, + feedback: PrezelChipFeedback = PrezelChipFeedback.DEFAULT, + config: PrezelChipDefault = PrezelChipDefaults.getDefault( + iconOnly = false, + type = type, + size = size, + interaction = interaction, + feedback = feedback, + ), ) { - val hasText = text != null - val hasIcon = iconResId != null - val iconOnly = hasIcon && !hasText - require(hasText || hasIcon) { "Chip은 text 또는 icon 중 하나는 반드시 필요합니다." } - - Surface( + PrezelChipLayout( modifier = modifier, - shape = style.shape(), - color = style.containerColor(iconOnly = iconOnly), - border = style.borderStroke(), - ) { - CompositionLocalProvider( - LocalTextStyle provides style.textStyle(), - LocalContentColor provides style.contentColor(), - ) { - Row( - modifier = Modifier.padding(style.contentPadding(iconOnly = iconOnly)), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - iconResId?.let { resId -> - PrezelChipIcon(iconResId = resId, style = style) - } - - if (hasText) { - if (hasIcon) { - Spacer(modifier = Modifier.width(style.iconTextSpacing())) - } - Text(text = text) - } - } - } - } + text = text, + iconResId = iconResId, + config = config, + ) } -@Composable -fun PrezelChip( - modifier: Modifier = Modifier, - text: String? = null, - @DrawableRes iconResId: Int? = null, - style: PrezelChipStyle = PrezelChipStyle(), - customColors: PrezelChipColors = LocalPrezelChipColors.current, -) { - CompositionLocalProvider( - LocalPrezelChipColors provides customColors, - ) { - PrezelChip( - modifier = modifier, - text = text, - iconResId = iconResId, - style = style, - ) - } -} - -@Composable -private fun PrezelChipIcon( - @DrawableRes iconResId: Int, - style: PrezelChipStyle, - modifier: Modifier = Modifier, -) { - Icon( - painter = painterResource(id = iconResId), - contentDescription = null, - modifier = modifier.size(style.iconSize()), - ) +private class PrezelChipTypeProvider : PreviewParameterProvider { + override val values: Sequence = PrezelChipType.entries.asSequence() } @BasicPreview @Composable -private fun PrezelChipSizePreview() { - PreviewSection( - title = "Chip / Size", - description = "Chip의 크기를 조절합니다.", - ) { - PrezelChipType.entries.forEach { type -> - PrezelChipSize.entries.forEach { size -> - PreviewValueRow( - name = type.name, - valueLabel = size.name, - ) { - PrezelChip( - text = "Label", - iconResId = PrezelIcons.Blank, - style = PrezelChipStyle(type = type, size = size), - ) - } - } +private fun PrezelChipPreview( + @PreviewParameter(PrezelChipTypeProvider::class) type: PrezelChipType, +) { + PreviewSection(title = "Chip - $type") { + Text(text = "Size", style = PrezelTheme.typography.title2Medium, color = PrezelTheme.colors.textLarge) + PreviewValueRow(name = "Regular") { + PrezelChip( + text = "Label", + iconResId = PrezelIcons.Blank, + type = type, + size = PrezelChipSize.REGULAR, + ) + } + PreviewValueRow(name = "Small") { + PrezelChip( + text = "Label", + iconResId = PrezelIcons.Blank, + type = type, + size = PrezelChipSize.SMALL, + ) } - } -} -@BasicPreview -@Composable -private fun PrezelChipInteractionPreview() { - PreviewSection( - title = "Chip / Interaction", - description = "Chip의 상호작용 상태를 조절합니다.", - ) { - PrezelChipType.entries.forEach { type -> - PrezelChipInteraction.entries.forEach { interaction -> - PreviewValueRow( - name = type.name, - valueLabel = interaction.name, - ) { - PrezelChip( - text = "Label", - iconResId = PrezelIcons.Blank, - style = PrezelChipStyle(type = type, interaction = interaction), - ) - } - } + Text(text = "Interaction", style = PrezelTheme.typography.title2Medium, color = PrezelTheme.colors.textLarge) + PreviewValueRow(name = "Default") { + PrezelChip( + text = "Label", + iconResId = PrezelIcons.Blank, + type = type, + interaction = PrezelChipInteraction.DEFAULT, + ) + } + PreviewValueRow(name = "Active") { + PrezelChip( + text = "Label", + iconResId = PrezelIcons.Blank, + type = type, + interaction = PrezelChipInteraction.ACTIVE, + ) + } + PreviewValueRow(name = "Disabled") { + PrezelChip( + text = "Label", + iconResId = PrezelIcons.Blank, + type = type, + interaction = PrezelChipInteraction.DISABLED, + ) } - } -} -@BasicPreview -@Composable -private fun PrezelChipFeedbackPreview() { - PreviewSection( - title = "Chip / Feedback", - description = "Chip의 피드백 상태를 조절합니다.", - ) { - PrezelChipType.entries.forEach { type -> - PrezelChipFeedback.entries.forEach { feedback -> - PreviewValueRow( - name = type.name, - valueLabel = feedback.name, - ) { - PrezelChip( - text = "Label", - iconResId = PrezelIcons.Blank, - style = PrezelChipStyle(type = type, feedback = feedback), - ) - } - } + Text(text = "Feedback", style = PrezelTheme.typography.title2Medium, color = PrezelTheme.colors.textLarge) + PreviewValueRow(name = "Default") { + PrezelChip( + text = "Label", + iconResId = PrezelIcons.Blank, + type = type, + feedback = PrezelChipFeedback.DEFAULT, + ) + } + PreviewValueRow(name = "Bad") { + PrezelChip( + text = "Label", + iconResId = PrezelIcons.Blank, + type = type, + feedback = PrezelChipFeedback.BAD, + ) } } } @BasicPreview @Composable -private fun PrezelChipCustomPreview() { - PreviewSection( - title = "Chip / Custom", - description = "사용자 정의된 색 지정이 가능합니다.", - ) { +private fun PrezelChipCustomPreview( + @PreviewParameter(PrezelChipTypeProvider::class) type: PrezelChipType, +) { + PreviewSection(title = "Custom Chip - $type") { PreviewRow { PrezelChip( - text = "느려요", + text = "Label", iconResId = PrezelIcons.Blank, - style = PrezelChipStyle( - type = PrezelChipType.FILLED, - size = PrezelChipSize.REGULAR, - interaction = PrezelChipInteraction.DISABLED, - feedback = PrezelChipFeedback.BAD, - ), - customColors = PrezelChipColors( - containerColor = PrezelTheme.colors.feedbackWarningSmall, - contentColor = PrezelTheme.colors.feedbackWarningRegular, + config = PrezelChipDefaults.getDefault( + iconOnly = false, + type = type, + containerColor = PrezelTheme.colors.accentPurpleSmall, + iconColor = PrezelTheme.colors.accentPurpleRegular, + textColor = PrezelTheme.colors.accentPurpleRegular, + borderColor = PrezelTheme.colors.accentPurpleRegular, ), ) PrezelChip( - text = "빨라요", - iconResId = null, - style = PrezelChipStyle( - type = PrezelChipType.OUTLINED, - size = PrezelChipSize.REGULAR, - interaction = PrezelChipInteraction.DEFAULT, - feedback = PrezelChipFeedback.DEFAULT, - ), - customColors = PrezelChipColors( - containerColor = PrezelTheme.colors.feedbackBadSmall, - contentColor = PrezelTheme.colors.feedbackBadRegular, + text = "Label", + iconResId = PrezelIcons.Blank, + config = PrezelChipDefaults.getDefault( + iconOnly = false, + type = type, + containerColor = PrezelTheme.colors.accentTealSmall, + iconColor = PrezelTheme.colors.accentTealRegular, + textColor = PrezelTheme.colors.accentTealRegular, + borderColor = PrezelTheme.colors.accentTealRegular, ), ) PrezelChip( - text = "적당해요", - iconResId = null, - style = PrezelChipStyle( - type = PrezelChipType.FILLED, - size = PrezelChipSize.SMALL, - interaction = PrezelChipInteraction.ACTIVE, - feedback = PrezelChipFeedback.DEFAULT, - ), - customColors = PrezelChipColors( - containerColor = Color(0xFFDBFFF6), - contentColor = Color(0xFF00A37A), + text = "Label", + iconResId = PrezelIcons.Blank, + config = PrezelChipDefaults.getDefault( + iconOnly = false, + type = type, + containerColor = PrezelTheme.colors.accentMagentaSmall, + iconColor = PrezelTheme.colors.accentMagentaRegular, + textColor = PrezelTheme.colors.accentMagentaRegular, + borderColor = PrezelTheme.colors.accentMagentaRegular, ), ) } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChipStyle.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChipStyle.kt deleted file mode 100644 index 2629ca56..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChipStyle.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.team.prezel.core.designsystem.component.chip - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable -import androidx.compose.runtime.staticCompositionLocalOf -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 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.foundation.typography.PrezelTypography -import com.team.prezel.core.designsystem.theme.PrezelTheme - -internal val LocalPrezelChipColors = staticCompositionLocalOf { PrezelChipColors() } - -enum class PrezelChipType { - FILLED, - OUTLINED, -} - -enum class PrezelChipInteraction { - DEFAULT, - ACTIVE, - DISABLED, -} - -enum class PrezelChipFeedback { - DEFAULT, - BAD, -} - -enum class PrezelChipSize { - SMALL, - REGULAR, -} - -@Immutable -data class PrezelChipColors( - val containerColor: Color = Color.Unspecified, - val contentColor: Color = Color.Unspecified, -) - -@Immutable -data class PrezelChipStyle( - val type: PrezelChipType = PrezelChipType.FILLED, - val size: PrezelChipSize = PrezelChipSize.REGULAR, - val interaction: PrezelChipInteraction = PrezelChipInteraction.DEFAULT, - val feedback: PrezelChipFeedback = PrezelChipFeedback.DEFAULT, -) { - @Composable - internal fun shape(shapes: PrezelShapes = PrezelTheme.shapes): Shape = - when (size) { - PrezelChipSize.SMALL -> shapes.V4 - PrezelChipSize.REGULAR -> shapes.V8 - } - - @Composable - internal fun borderStroke( - colors: PrezelColors = PrezelTheme.colors, - stroke: PrezelStroke = PrezelTheme.stroke, - chipColors: PrezelChipColors = LocalPrezelChipColors.current, - ): BorderStroke? { - if (type == PrezelChipType.FILLED) return null - - val borderColor = - when { - chipColors.contentColor != Color.Unspecified -> chipColors.contentColor - feedback == PrezelChipFeedback.BAD -> colors.feedbackBadRegular - interaction == PrezelChipInteraction.ACTIVE -> colors.interactiveRegular - interaction == PrezelChipInteraction.DISABLED -> colors.borderRegular - else -> colors.borderMedium - } - - return BorderStroke( - width = stroke.V1, - color = borderColor, - ) - } - - @Composable - internal fun textStyle(typography: PrezelTypography = PrezelTheme.typography): TextStyle = - when (size) { - PrezelChipSize.SMALL -> typography.caption2Regular - PrezelChipSize.REGULAR -> typography.caption1Regular - } - - @Composable - internal fun containerColor( - iconOnly: Boolean, - colors: PrezelColors = PrezelTheme.colors, - chipColors: PrezelChipColors = LocalPrezelChipColors.current, - ): Color { - if (type == PrezelChipType.OUTLINED && iconOnly) { - return Color.Transparent - } - - return when { - chipColors.containerColor != Color.Unspecified -> chipColors.containerColor - feedback == PrezelChipFeedback.BAD -> colors.feedbackBadSmall - interaction == PrezelChipInteraction.ACTIVE -> colors.interactiveXSmall - interaction == PrezelChipInteraction.DISABLED -> colors.bgLarge - else -> { - when (type) { - PrezelChipType.FILLED -> colors.bgMedium - PrezelChipType.OUTLINED -> colors.bgRegular - } - } - } - } - - @Composable - internal fun contentColor( - colors: PrezelColors = PrezelTheme.colors, - chipColors: PrezelChipColors = LocalPrezelChipColors.current, - ): Color = - when { - chipColors.contentColor != Color.Unspecified -> chipColors.contentColor - feedback == PrezelChipFeedback.BAD -> colors.feedbackBadRegular - interaction == PrezelChipInteraction.ACTIVE -> colors.interactiveRegular - interaction == PrezelChipInteraction.DISABLED -> colors.iconDisabled - else -> colors.iconRegular - } - - @Composable - internal fun contentPadding( - iconOnly: Boolean, - spacing: PrezelSpacing = PrezelTheme.spacing, - ): PaddingValues { - if (iconOnly) { - val all = when (size) { - PrezelChipSize.SMALL -> spacing.V6 - PrezelChipSize.REGULAR -> spacing.V8 - } - return PaddingValues(all = all) - } - - val horizontal = when (size) { - PrezelChipSize.SMALL -> spacing.V6 - PrezelChipSize.REGULAR -> spacing.V8 - } - - val vertical = when (size) { - PrezelChipSize.SMALL -> spacing.V4 - PrezelChipSize.REGULAR -> spacing.V6 - } - - return PaddingValues(horizontal = horizontal, vertical = vertical) - } - - @Composable - internal fun iconTextSpacing(spacing: PrezelSpacing = PrezelTheme.spacing): Dp = - when (size) { - PrezelChipSize.REGULAR -> spacing.V4 - PrezelChipSize.SMALL -> spacing.V2 - } - - internal fun iconSize(): Dp = - when (size) { - PrezelChipSize.SMALL -> 14.dp - PrezelChipSize.REGULAR -> 16.dp - } -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelIconChip.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelIconChip.kt index 3e7e6e17..368dac74 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelIconChip.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelIconChip.kt @@ -1,91 +1,158 @@ package com.team.prezel.core.designsystem.component.chip import androidx.annotation.DrawableRes +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipDefault +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipDefaults +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipFeedback +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipInteraction +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipLayout +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipSize +import com.team.prezel.core.designsystem.component.chip.config.PrezelChipType import com.team.prezel.core.designsystem.icon.PrezelIcons import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewRow import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.preview.PreviewValueRow +import com.team.prezel.core.designsystem.theme.PrezelTheme @Composable fun PrezelIconChip( @DrawableRes iconResId: Int, modifier: Modifier = Modifier, - style: PrezelChipStyle = PrezelChipStyle(), + type: PrezelChipType = PrezelChipType.FILLED, + size: PrezelChipSize = PrezelChipSize.REGULAR, + interaction: PrezelChipInteraction = PrezelChipInteraction.DEFAULT, + feedback: PrezelChipFeedback = PrezelChipFeedback.DEFAULT, + config: PrezelChipDefault = PrezelChipDefaults.getDefault( + iconOnly = true, + type = type, + size = size, + interaction = interaction, + feedback = feedback, + ), ) { - PrezelChip( + PrezelChipLayout( modifier = modifier, + text = null, iconResId = iconResId, - style = style, + config = config, ) } -@BasicPreview -@Composable -private fun PrezelChipSizePreview() { - PreviewSection( - title = "Chip / Size", - description = "Chip의 크기를 조절합니다.", - ) { - PrezelChipType.entries.forEach { type -> - PrezelChipSize.entries.forEach { size -> - PreviewValueRow( - name = type.name, - valueLabel = size.name, - ) { - PrezelIconChip( - iconResId = PrezelIcons.Blank, - style = PrezelChipStyle(type = type, size = size), - ) - } - } - } - } +private class PrezelIconChipTypeProvider : PreviewParameterProvider { + override val values: Sequence = PrezelChipType.entries.asSequence() } @BasicPreview @Composable -private fun PrezelChipInteractionPreview() { - PreviewSection( - title = "Chip / Interaction", - description = "Chip의 상호작용 상태를 조절합니다.", - ) { - PrezelChipType.entries.forEach { type -> - PrezelChipInteraction.entries.forEach { interaction -> - PreviewValueRow( - name = type.name, - valueLabel = interaction.name, - ) { - PrezelIconChip( - iconResId = PrezelIcons.Blank, - style = PrezelChipStyle(type = type, interaction = interaction), - ) - } - } +private fun PrezelChipPreview( + @PreviewParameter(PrezelIconChipTypeProvider::class) type: PrezelChipType, +) { + PreviewSection(title = "Chip - $type") { + Text(text = "Size", style = PrezelTheme.typography.title2Medium, color = PrezelTheme.colors.textLarge) + PreviewValueRow(name = "Regular") { + PrezelIconChip( + iconResId = PrezelIcons.Blank, + type = type, + size = PrezelChipSize.REGULAR, + ) + } + PreviewValueRow(name = "Small") { + PrezelIconChip( + iconResId = PrezelIcons.Blank, + type = type, + size = PrezelChipSize.SMALL, + ) + } + + Text(text = "Interaction", style = PrezelTheme.typography.title2Medium, color = PrezelTheme.colors.textLarge) + PreviewValueRow(name = "Default") { + PrezelIconChip( + iconResId = PrezelIcons.Blank, + type = type, + interaction = PrezelChipInteraction.DEFAULT, + ) + } + PreviewValueRow(name = "Active") { + PrezelIconChip( + iconResId = PrezelIcons.Blank, + type = type, + interaction = PrezelChipInteraction.ACTIVE, + ) + } + PreviewValueRow(name = "Disabled") { + PrezelIconChip( + iconResId = PrezelIcons.Blank, + type = type, + interaction = PrezelChipInteraction.DISABLED, + ) + } + + Text(text = "Feedback", style = PrezelTheme.typography.title2Medium, color = PrezelTheme.colors.textLarge) + PreviewValueRow(name = "Default") { + PrezelIconChip( + iconResId = PrezelIcons.Blank, + type = type, + feedback = PrezelChipFeedback.DEFAULT, + ) + } + PreviewValueRow(name = "Bad") { + PrezelIconChip( + iconResId = PrezelIcons.Blank, + type = type, + feedback = PrezelChipFeedback.BAD, + ) } } } @BasicPreview @Composable -private fun PrezelChipFeedbackPreview() { - PreviewSection( - title = "Chip / Feedback", - description = "Chip의 피드백 상태를 조절합니다.", - ) { - PrezelChipType.entries.forEach { type -> - PrezelChipFeedback.entries.forEach { feedback -> - PreviewValueRow( - name = type.name, - valueLabel = feedback.name, - ) { - PrezelIconChip( - iconResId = PrezelIcons.Blank, - style = PrezelChipStyle(type = type, feedback = feedback), - ) - } - } +private fun PrezelChipCustomPreview( + @PreviewParameter(PrezelIconChipTypeProvider::class) type: PrezelChipType, +) { + PreviewSection(title = "Custom Chip - $type") { + PreviewRow { + PrezelIconChip( + iconResId = PrezelIcons.Blank, + config = PrezelChipDefaults.getDefault( + iconOnly = true, + type = type, + containerColor = PrezelTheme.colors.accentPurpleSmall, + iconColor = PrezelTheme.colors.accentPurpleRegular, + textColor = PrezelTheme.colors.accentPurpleRegular, + borderColor = PrezelTheme.colors.accentPurpleRegular, + ), + ) + + PrezelIconChip( + iconResId = PrezelIcons.Blank, + config = PrezelChipDefaults.getDefault( + iconOnly = true, + type = type, + containerColor = PrezelTheme.colors.accentTealSmall, + iconColor = PrezelTheme.colors.accentTealRegular, + textColor = PrezelTheme.colors.accentTealRegular, + borderColor = PrezelTheme.colors.accentTealRegular, + ), + ) + + PrezelIconChip( + iconResId = PrezelIcons.Blank, + config = PrezelChipDefaults.getDefault( + iconOnly = true, + type = type, + containerColor = PrezelTheme.colors.accentMagentaSmall, + iconColor = PrezelTheme.colors.accentMagentaRegular, + textColor = PrezelTheme.colors.accentMagentaRegular, + borderColor = PrezelTheme.colors.accentMagentaRegular, + ), + ) } } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/config/PrezelChipDefaults.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/config/PrezelChipDefaults.kt new file mode 100644 index 00000000..c839bbef --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/config/PrezelChipDefaults.kt @@ -0,0 +1,200 @@ +package com.team.prezel.core.designsystem.component.chip.config + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +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 androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.theme.PrezelTheme + +@Immutable +data class PrezelChipDefault( + val shape: Shape, + val textStyle: TextStyle, + val containerColor: Color, + val iconColor: Color, + val textColor: Color, + val borderColor: Color?, + val contentPadding: PaddingValues, + val iconTextSpacing: Dp, + val iconSize: Dp, +) + +object PrezelChipDefaults { + @Composable + fun getDefault( + iconOnly: Boolean, + type: PrezelChipType = PrezelChipType.FILLED, + size: PrezelChipSize = PrezelChipSize.REGULAR, + interaction: PrezelChipInteraction = PrezelChipInteraction.DEFAULT, + feedback: PrezelChipFeedback = PrezelChipFeedback.DEFAULT, + shape: Shape = getShape(size = size), + textStyle: TextStyle = getTextStyle(size = size), + containerColor: Color = getContainerColor( + iconOnly = iconOnly, + type = type, + interaction = interaction, + feedback = feedback, + ), + iconColor: Color = getIconColor( + type = type, + interaction = interaction, + feedback = feedback, + ), + textColor: Color = getTextColor( + type = type, + interaction = interaction, + feedback = feedback, + ), + borderColor: Color? = getBorderColor( + type = type, + interaction = interaction, + feedback = feedback, + ), + contentPadding: PaddingValues = getContentPadding(size = size, iconOnly = iconOnly), + iconTextSpacing: Dp = getIconTextSpacing(size = size), + iconSize: Dp = getIconSize(size = size), + ) = PrezelChipDefault( + shape = shape, + textStyle = textStyle, + containerColor = containerColor, + iconColor = iconColor, + textColor = textColor, + borderColor = if (type == PrezelChipType.OUTLINED) borderColor else null, + contentPadding = contentPadding, + iconTextSpacing = iconTextSpacing, + iconSize = iconSize, + ) + + @Composable + private fun getShape(size: PrezelChipSize): Shape = + when (size) { + PrezelChipSize.SMALL -> PrezelTheme.shapes.V4 + PrezelChipSize.REGULAR -> PrezelTheme.shapes.V8 + } + + @Composable + private fun getBorderColor( + type: PrezelChipType, + interaction: PrezelChipInteraction, + feedback: PrezelChipFeedback, + ): Color? { + if (type == PrezelChipType.FILLED) return null + + return when { + feedback == PrezelChipFeedback.BAD -> PrezelTheme.colors.feedbackBadRegular + interaction == PrezelChipInteraction.ACTIVE -> PrezelTheme.colors.interactiveRegular + interaction == PrezelChipInteraction.DISABLED -> PrezelTheme.colors.borderRegular + else -> PrezelTheme.colors.borderMedium + } + } + + @Composable + private fun getTextStyle(size: PrezelChipSize): TextStyle = + when (size) { + PrezelChipSize.SMALL -> PrezelTheme.typography.caption2Regular + PrezelChipSize.REGULAR -> PrezelTheme.typography.caption1Regular + } + + @Composable + private fun getContainerColor( + iconOnly: Boolean, + type: PrezelChipType, + interaction: PrezelChipInteraction, + feedback: PrezelChipFeedback, + ): Color { + if (type == PrezelChipType.OUTLINED && iconOnly) { + return Color.Transparent + } + + return when { + feedback == PrezelChipFeedback.BAD -> PrezelTheme.colors.feedbackBadSmall + interaction == PrezelChipInteraction.ACTIVE -> PrezelTheme.colors.interactiveXSmall + interaction == PrezelChipInteraction.DISABLED -> PrezelTheme.colors.bgDisabled + else -> { + when (type) { + PrezelChipType.FILLED -> PrezelTheme.colors.bgLarge + PrezelChipType.OUTLINED -> PrezelTheme.colors.bgRegular + } + } + } + } + + @Composable + private fun getIconColor( + type: PrezelChipType, + interaction: PrezelChipInteraction, + feedback: PrezelChipFeedback, + ): Color = + when { + feedback == PrezelChipFeedback.BAD -> PrezelTheme.colors.feedbackBadRegular + interaction == PrezelChipInteraction.ACTIVE -> PrezelTheme.colors.interactiveRegular + interaction == PrezelChipInteraction.DISABLED -> PrezelTheme.colors.iconDisabled + else -> { + when (type) { + PrezelChipType.FILLED -> PrezelTheme.colors.iconMedium + PrezelChipType.OUTLINED -> PrezelTheme.colors.iconRegular + } + } + } + + @Composable + private fun getTextColor( + type: PrezelChipType, + interaction: PrezelChipInteraction, + feedback: PrezelChipFeedback, + ): Color = + when { + feedback == PrezelChipFeedback.BAD -> PrezelTheme.colors.feedbackBadRegular + interaction == PrezelChipInteraction.ACTIVE -> PrezelTheme.colors.interactiveRegular + interaction == PrezelChipInteraction.DISABLED -> PrezelTheme.colors.textDisabled + else -> { + when (type) { + PrezelChipType.FILLED -> PrezelTheme.colors.textMedium + PrezelChipType.OUTLINED -> PrezelTheme.colors.textRegular + } + } + } + + @Composable + private fun getContentPadding( + size: PrezelChipSize, + iconOnly: Boolean, + ): PaddingValues { + if (iconOnly) { + val all = when (size) { + PrezelChipSize.SMALL -> PrezelTheme.spacing.V6 + PrezelChipSize.REGULAR -> PrezelTheme.spacing.V8 + } + return PaddingValues(all = all) + } + + val horizontal = when (size) { + PrezelChipSize.SMALL -> PrezelTheme.spacing.V6 + PrezelChipSize.REGULAR -> PrezelTheme.spacing.V8 + } + + val vertical = when (size) { + PrezelChipSize.SMALL -> PrezelTheme.spacing.V4 + PrezelChipSize.REGULAR -> PrezelTheme.spacing.V6 + } + + return PaddingValues(horizontal = horizontal, vertical = vertical) + } + + @Composable + private fun getIconTextSpacing(size: PrezelChipSize): Dp = + when (size) { + PrezelChipSize.REGULAR -> PrezelTheme.spacing.V4 + PrezelChipSize.SMALL -> PrezelTheme.spacing.V2 + } + + private fun getIconSize(size: PrezelChipSize): Dp = + when (size) { + PrezelChipSize.SMALL -> 14.dp + PrezelChipSize.REGULAR -> 16.dp + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/config/PrezelChipLayout.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/config/PrezelChipLayout.kt new file mode 100644 index 00000000..1bfbb4d6 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/config/PrezelChipLayout.kt @@ -0,0 +1,79 @@ +package com.team.prezel.core.designsystem.component.chip.config + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.BorderStroke +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.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +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.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.Dp +import com.team.prezel.core.designsystem.theme.PrezelTheme + +@Composable +internal fun PrezelChipLayout( + modifier: Modifier = Modifier, + text: String?, + @DrawableRes iconResId: Int?, + config: PrezelChipDefault, +) { + require(text != null || iconResId != null) { "Chip은 text 또는 icon 중 하나는 반드시 필요합니다." } + + Surface( + modifier = modifier, + shape = config.shape, + color = config.containerColor, + border = config.borderColor?.let { color -> + BorderStroke(width = PrezelTheme.stroke.V1, color = color) + }, + ) { + Row( + modifier = Modifier.padding(config.contentPadding), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + iconResId?.let { resId -> + PrezelChipIcon( + iconResId = resId, + iconSize = config.iconSize, + tint = config.iconColor, + ) + } + + if (text != null) { + if (iconResId != null) { + Spacer(modifier = Modifier.width(config.iconTextSpacing)) + } + Text( + text = text, + color = config.textColor, + style = config.textStyle, + ) + } + } + } +} + +@Composable +private fun PrezelChipIcon( + @DrawableRes iconResId: Int, + iconSize: Dp, + tint: Color, + modifier: Modifier = Modifier, +) { + Icon( + painter = painterResource(id = iconResId), + contentDescription = null, + modifier = modifier.size(iconSize), + tint = tint, + ) +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/config/PrezelChipModels.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/config/PrezelChipModels.kt new file mode 100644 index 00000000..7f150a8b --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/config/PrezelChipModels.kt @@ -0,0 +1,62 @@ +package com.team.prezel.core.designsystem.component.chip.config + +import androidx.compose.runtime.Immutable + +/** + * 칩의 시각적 타입을 정의합니다. + * + * Chip의 배경, 테두리 표현 방식을 구분할 때 사용합니다. + */ +@Immutable +enum class PrezelChipType { + /** 배경이 채워진 칩입니다. */ + FILLED, + + /** 테두리가 있는 칩입니다. */ + OUTLINED, +} + +/** + * 칩의 크기를 정의합니다. + * + * 칩의 높이, 패딩, 아이콘 크기, 텍스트 스타일의 기준으로 사용합니다. + */ +@Immutable +enum class PrezelChipSize { + /** 작은 크기의 칩입니다. */ + SMALL, + + /** 기본 크기의 칩입니다. */ + REGULAR, +} + +/** + * 칩의 상호작용 상태를 정의합니다. + * + * 선택 여부나 비활성 상태에 따라 칩의 강조 수준을 표현할 때 사용합니다. + */ +@Immutable +enum class PrezelChipInteraction { + /** 기본 상태의 칩입니다. */ + DEFAULT, + + /** 활성화되어 강조된 상태의 칩입니다. */ + ACTIVE, + + /** 비활성화된 상태의 칩입니다. */ + DISABLED, +} + +/** + * 칩의 피드백 상태를 정의합니다. + * + * 일반 상태 외에 경고성 의미를 함께 전달해야 할 때 사용합니다. + */ +@Immutable +enum class PrezelChipFeedback { + /** 일반 상태의 칩입니다. */ + DEFAULT, + + /** 부정적 피드백을 표현하는 칩입니다. */ + BAD, +} 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 49120df1..80dfc477 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 @@ -27,9 +27,9 @@ internal object PrezelColorScheme { interactiveRegular = ColorTokens.Blue500, bgRegular = ColorTokens.Common0, bgMedium = ColorTokens.CoolGray10, - bgLarge = ColorTokens.CoolGray50, + bgLarge = ColorTokens.CoolGray100, bgScrim = ColorTokens.Common1000.copy(alpha = 0.32f), - bgDisabled = ColorTokens.CoolGray100, + bgDisabled = ColorTokens.CoolGray50, textSmall = ColorTokens.CoolGray300, textRegular = ColorTokens.CoolGray500, textMedium = ColorTokens.CoolGray700, diff --git a/Prezel/core/designsystem/src/main/res/drawable/core_designsystem_ic_script.xml b/Prezel/core/designsystem/src/main/res/drawable/core_designsystem_ic_script.xml new file mode 100644 index 00000000..7add81a2 --- /dev/null +++ b/Prezel/core/designsystem/src/main/res/drawable/core_designsystem_ic_script.xml @@ -0,0 +1,10 @@ + + +