diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/list/PrezelList.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/list/PrezelList.kt new file mode 100644 index 00000000..67184683 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/list/PrezelList.kt @@ -0,0 +1,157 @@ +package com.team.prezel.core.designsystem.component.list + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor +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.icon.PrezelIcons +import com.team.prezel.core.designsystem.preview.PreviewScaffold +import com.team.prezel.core.designsystem.preview.SectionTitle +import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.theme.PrezelTheme +import com.team.prezel.core.designsystem.util.drawDashBorder + +@Composable +fun PrezelList( + title: String, + modifier: Modifier = Modifier, + size: PrezelListSize = PrezelListSize.REGULAR, + nested: Boolean = false, + leadingContent: @Composable (RowScope.() -> Unit)? = null, + trailingContent: @Composable (RowScope.() -> Unit)? = null, +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(prezelListContentPadding(size, nested)), + verticalAlignment = Alignment.CenterVertically, + ) { + leadingContent?.let { content -> + content() + Spacer(modifier = Modifier.width(prezelListIconTextSpacing(size))) + } + + PrezelListTitle(title, size) + + trailingContent?.let { content -> + Spacer(modifier = Modifier.width(prezelListTextTrailingSpacing(size))) + + Row( + horizontalArrangement = Arrangement.spacedBy(prezelListTrailingIconSpacing(size)), + verticalAlignment = Alignment.CenterVertically, + ) { + content() + } + } + } +} + +@Composable +private fun RowScope.PrezelListTitle( + title: String, + size: PrezelListSize, +) { + Text( + text = title, + modifier = Modifier.weight(1f), + maxLines = 1, + style = prezelListTextStyle(size), + color = LocalContentColor.current, + ) +} + +@ThemePreview +@Composable +private fun PrezelListSmallPreview() { + PrezelTheme { + PreviewScaffold { + PrezelListPreviewBySize(PrezelListSize.SMALL) + } + } +} + +@ThemePreview +@Composable +private fun PrezelListRegularPreview() { + PrezelTheme { + PreviewScaffold { + PrezelListPreviewBySize(PrezelListSize.REGULAR) + } + } +} + +@Composable +private fun PrezelListPreviewItem( + size: PrezelListSize, + nested: Boolean, + showLeadingContent: Boolean, + showTrailingContent: Boolean, +) { + val leadingContent: @Composable RowScope.() -> Unit = { + Icon( + painter = painterResource(id = PrezelIcons.Blank), + contentDescription = "leading", + ) + } + + val trailingContent: @Composable RowScope.() -> Unit = { + Icon( + painter = painterResource(id = PrezelIcons.Blank), + contentDescription = "trailing", + ) + Icon( + painter = painterResource(id = PrezelIcons.Blank), + contentDescription = "trailing", + ) + } + + PrezelList( + title = "Title", + size = size, + nested = nested, + leadingContent = if (showLeadingContent) leadingContent else null, + trailingContent = if (showTrailingContent) trailingContent else null, + modifier = Modifier.drawDashBorder(), + ) +} + +@Composable +private fun PrezelListPreviewBySize(size: PrezelListSize) { + SectionTitle(title = "PrezelList - $size") + Text( + text = "점선 테두리는 컴포넌트 경계를 의미하며,\nnested 상태별 leading/trailing 조합을 확인할 수 있습니다.", + style = PrezelTheme.typography.body3Regular, + color = PrezelTheme.colors.textMedium, + modifier = Modifier + .fillMaxWidth() + .background(Color.LightGray.copy(alpha = 0.5f)) + .padding(8.dp), + ) + + Text(text = "Nested: False", style = PrezelTheme.typography.body2Bold) + PrezelListPreviewItem(size, nested = false, showLeadingContent = true, showTrailingContent = true) + PrezelListPreviewItem(size, nested = false, showLeadingContent = true, showTrailingContent = false) + PrezelListPreviewItem(size, nested = false, showLeadingContent = false, showTrailingContent = true) + PrezelListPreviewItem(size, nested = false, showLeadingContent = false, showTrailingContent = false) + + Spacer(modifier = Modifier.height(8.dp)) + Text(text = "Nested: True", style = PrezelTheme.typography.body2Bold) + PrezelListPreviewItem(size, nested = true, showLeadingContent = true, showTrailingContent = true) + PrezelListPreviewItem(size, nested = true, showLeadingContent = true, showTrailingContent = false) + PrezelListPreviewItem(size, nested = true, showLeadingContent = false, showTrailingContent = true) + PrezelListPreviewItem(size, nested = true, showLeadingContent = false, showTrailingContent = false) +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/list/PrezelListStyle.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/list/PrezelListStyle.kt new file mode 100644 index 00000000..9c336252 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/list/PrezelListStyle.kt @@ -0,0 +1,73 @@ +package com.team.prezel.core.designsystem.component.list + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.Dp +import com.team.prezel.core.designsystem.foundation.number.PrezelSpacing +import com.team.prezel.core.designsystem.theme.PrezelTheme + +@Immutable +enum class PrezelListSize { + SMALL, + REGULAR, +} + +@Composable +internal fun prezelListContentPadding( + size: PrezelListSize, + nested: Boolean, + spacing: PrezelSpacing = PrezelTheme.spacing, +): PaddingValues { + if (nested) { + return PaddingValues(spacing.V0) + } + + val vertical = when (size) { + PrezelListSize.SMALL -> spacing.V10 + PrezelListSize.REGULAR -> spacing.V14 + } + + return PaddingValues( + horizontal = spacing.V12, + vertical = vertical, + ) +} + +@Composable +internal fun prezelListTextStyle(size: PrezelListSize): TextStyle = + when (size) { + PrezelListSize.SMALL -> PrezelTheme.typography.body3Medium + PrezelListSize.REGULAR -> PrezelTheme.typography.body2Medium + } + +@Composable +internal fun prezelListIconTextSpacing( + size: PrezelListSize, + spacing: PrezelSpacing = PrezelTheme.spacing, +): Dp = + when (size) { + PrezelListSize.SMALL -> spacing.V8 + PrezelListSize.REGULAR -> spacing.V12 + } + +@Composable +internal fun prezelListTextTrailingSpacing( + size: PrezelListSize, + spacing: PrezelSpacing = PrezelTheme.spacing, +): Dp = + when (size) { + PrezelListSize.SMALL -> spacing.V6 + PrezelListSize.REGULAR -> spacing.V12 + } + +@Composable +internal fun prezelListTrailingIconSpacing( + size: PrezelListSize, + spacing: PrezelSpacing = PrezelTheme.spacing, +): Dp = + when (size) { + PrezelListSize.SMALL -> spacing.V6 + PrezelListSize.REGULAR -> spacing.V8 + } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DrowDashBorder.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DrowDashBorder.kt new file mode 100644 index 00000000..8d09b336 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DrowDashBorder.kt @@ -0,0 +1,44 @@ +package com.team.prezel.core.designsystem.util + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.theme.PrezelTheme + +fun Modifier.drawDashBorder( + color: Color = Color.Gray, + width: Float = 1f, + interval: Float = 10f, + phase: Float = 0f, +) = this.drawBehind { + drawRoundRect( + color = color, + style = Stroke( + width = width, + pathEffect = PathEffect.dashPathEffect(floatArrayOf(interval, interval), phase), + ), + ) +} + +@ThemePreview +@Composable +private fun DrawDashBorderPreview() { + PrezelTheme { + Box( + modifier = Modifier + .fillMaxWidth() + .height(100.dp) + .padding(12.dp) + .drawDashBorder(), + ) + } +}