diff --git a/Prezel/.editorconfig b/Prezel/.editorconfig index e31becfc..8f5a7cb2 100644 --- a/Prezel/.editorconfig +++ b/Prezel/.editorconfig @@ -12,6 +12,7 @@ trim_trailing_whitespace = true ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = true ktlint_function_naming_ignore_when_annotated_with = Composable +ktlint_standard_backing-property-naming = disabled ktlint_standard_multiline-expression-wrapping = disabled ktlint_standard_function-signature = enabled ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 2 diff --git a/Prezel/app/src/main/java/com/team/prezel/ui/PrezelApp.kt b/Prezel/app/src/main/java/com/team/prezel/ui/PrezelApp.kt index a957215c..1e67f936 100644 --- a/Prezel/app/src/main/java/com/team/prezel/ui/PrezelApp.kt +++ b/Prezel/app/src/main/java/com/team/prezel/ui/PrezelApp.kt @@ -17,7 +17,6 @@ import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider import androidx.navigation3.ui.NavDisplay import com.team.prezel.core.designsystem.component.PrezelNavigationScaffold -import com.team.prezel.core.designsystem.icon.IconSource import com.team.prezel.core.navigation.LocalNavigator import com.team.prezel.core.navigation.Navigator import com.team.prezel.core.navigation.ProvideSharedTransitionScope @@ -68,11 +67,11 @@ private fun PrezelAppContent( snackbarHostState = snackbarHostState, navigationItems = { MAIN_NAV_ITEMS.forEach { (key, item) -> - item( + Item( selected = key == appState.navigationState.currentTopLevelKey, onClick = { navigator.navigate(key) }, label = stringResource(item.titleTextId), - icon = IconSource(item.iconRes), + iconResId = item.iconRes, ) } }, diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelAccordion.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelAccordion.kt index 9f7f5dcb..1a2f7994 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelAccordion.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelAccordion.kt @@ -32,13 +32,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.R -import com.team.prezel.core.designsystem.component.button.PrezelHyperlinkButton +import com.team.prezel.core.designsystem.component.actions.button.PrezelHyperlinkButton import com.team.prezel.core.designsystem.component.list.PrezelList import com.team.prezel.core.designsystem.component.list.PrezelListSize import com.team.prezel.core.designsystem.foundation.typography.PrezelTextStyles 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.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewColumn +import com.team.prezel.core.designsystem.preview.PreviewSurface import com.team.prezel.core.designsystem.theme.PrezelTheme import com.team.prezel.core.designsystem.util.drawDashBorder @@ -129,11 +130,11 @@ private fun PrezelAccordionContent( } } -@ThemePreview +@BasicPreview @Composable private fun PrezelAccordionPreview() { - PrezelTheme { - PreviewScaffold { + PreviewSurface { + PreviewColumn(scrollable = true) { Column(verticalArrangement = Arrangement.spacedBy(40.dp)) { PrezelAccordion( title = "Collapsed Accordion", diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelAvatar.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelAvatar.kt index 1a7f12e0..7a95d136 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelAvatar.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelAvatar.kt @@ -2,14 +2,10 @@ package com.team.prezel.core.designsystem.component import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon -import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -24,7 +20,9 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.R import com.team.prezel.core.designsystem.foundation.number.PrezelStroke -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +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 @@ -109,50 +107,52 @@ private fun DefaultAvatarIcon( ) } -@ThemePreview +@BasicPreview @Composable -private fun PrezelAvatarSizePreview() { - PrezelTheme { - Surface(color = PrezelTheme.colors.bgRegular) { - Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier.padding(16.dp), - ) { - PrezelAvatar( - imageUrl = null, - contentDescription = "기본 아바타", - size = PrezelAvatarSize.SMALL, - ) - PrezelAvatar( - imageUrl = null, - contentDescription = "기본 아바타", - size = PrezelAvatarSize.REGULAR, - ) - } +private fun PrezelAvatarPreview() { + PreviewSection( + title = "Avatar", + description = "Avatar는 사용자를 대신하는 그래픽 요소입니다.", + ) { + PreviewValueRow( + name = "Regular", + valueLabel = "기본 아바타", + ) { + PrezelAvatar( + imageUrl = null, + contentDescription = "기본 아바타", + size = PrezelAvatarSize.REGULAR, + ) } - } -} - -@ThemePreview -@Composable -private fun PrezelAvatarTypePreview() { - PrezelTheme { - Surface(color = PrezelTheme.colors.bgRegular) { - Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier.padding(16.dp), - ) { - PrezelAvatar( - imageUrl = null, - contentDescription = "Default Type", - size = PrezelAvatarSize.SMALL, - ) - PrezelAvatar( - imageUrl = "https://picsum.photos/200", - contentDescription = "Image Type", - size = PrezelAvatarSize.SMALL, - ) - } + PreviewValueRow( + name = "Regular", + valueLabel = "이미지", + ) { + PrezelAvatar( + imageUrl = "https://picsum.photos/200", + contentDescription = "기본 아바타", + size = PrezelAvatarSize.REGULAR, + ) + } + PreviewValueRow( + name = "Small", + valueLabel = "기본 아바타", + ) { + PrezelAvatar( + imageUrl = null, + contentDescription = "기본 아바타", + size = PrezelAvatarSize.SMALL, + ) + } + PreviewValueRow( + name = "Small", + valueLabel = "이미지", + ) { + PrezelAvatar( + imageUrl = "https://picsum.photos/200", + contentDescription = "이미지", + size = PrezelAvatarSize.SMALL, + ) } } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelCheckbox.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelCheckbox.kt index 926a7aba..a9b31962 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelCheckbox.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelCheckbox.kt @@ -1,16 +1,10 @@ package com.team.prezel.core.designsystem.component -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.selection.toggleable import androidx.compose.material3.Icon -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -23,9 +17,10 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.R -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.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow import com.team.prezel.core.designsystem.theme.PrezelTheme import com.team.prezel.core.designsystem.util.drawDashBorder @@ -81,81 +76,30 @@ fun PrezelCheckbox( } } -@ThemePreview +@BasicPreview @Composable -private fun PrezelRegularCheckboxPreview() { - PrezelTheme { - Column( - modifier = Modifier - .background(PrezelTheme.colors.bgRegular) - .padding(16.dp), - ) { - CheckboxRowPreview(title = "Regular Checkbox") { - var checkState by remember { mutableStateOf(true) } +private fun PrezelCheckboxPreview() { + var checkState by remember { mutableStateOf(true) } - PrezelCheckbox( - checked = checkState, - modifier = Modifier.drawDashBorder(), - size = CheckboxSize.REGULAR, - onCheckedChange = { checkState = it }, - ) - - PrezelCheckbox( - checked = !checkState, - modifier = Modifier.drawDashBorder(), - size = CheckboxSize.REGULAR, - onCheckedChange = { checkState = it }, - ) - } + PreviewSection( + title = "Checkbox", + description = "Checkbox는 목록에서 선택할 항목이 여러 개 있을 때 사용됩니다.", + ) { + PreviewValueRow(name = "Regular") { + PrezelCheckbox( + checked = checkState, + modifier = Modifier.drawDashBorder(), + size = CheckboxSize.REGULAR, + onCheckedChange = { checkState = it }, + ) } - } -} - -@ThemePreview -@Composable -private fun PrezelLargeCheckboxPreview() { - PrezelTheme { - Column( - modifier = Modifier - .background(PrezelTheme.colors.bgRegular) - .padding(16.dp), - ) { - CheckboxRowPreview(title = "Large Checkbox") { - var checkState by remember { mutableStateOf(true) } - - PrezelCheckbox( - checked = checkState, - modifier = Modifier.drawDashBorder(), - size = CheckboxSize.LARGE, - onCheckedChange = { checkState = it }, - ) - - PrezelCheckbox( - checked = !checkState, - modifier = Modifier.drawDashBorder(), - size = CheckboxSize.LARGE, - onCheckedChange = { checkState = it }, - ) - } + PreviewValueRow(name = "Large") { + PrezelCheckbox( + checked = !checkState, + modifier = Modifier.drawDashBorder(), + size = CheckboxSize.LARGE, + onCheckedChange = { checkState = it }, + ) } } } - -@Composable -private fun CheckboxRowPreview( - title: String, - content: @Composable RowScope.() -> Unit, -) { - Column(modifier = Modifier.padding(bottom = 16.dp)) { - Text( - text = title, - style = PrezelTextStyles.Caption2Regular.toTextStyle(), - color = PrezelTheme.colors.textLarge, - modifier = Modifier.padding(bottom = 4.dp), - ) - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - content = content, - ) - } -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelDivider.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelDivider.kt index 094c0691..6085337e 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelDivider.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelDivider.kt @@ -1,18 +1,18 @@ package com.team.prezel.core.designsystem.component -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Text import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -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.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow import com.team.prezel.core.designsystem.theme.PrezelTheme enum class PrezelDividerType( @@ -59,24 +59,38 @@ private fun PrezelDivider( } } -@ThemePreview +@BasicPreview @Composable -private fun PrezelDividerPreview() { - PrezelTheme { - PreviewScaffold { - SectionTitle(title = "PrezelDivider - Horizontal") - Text("PrezelDividerType.DEFAULT") - PrezelHorizontalDivider(type = PrezelDividerType.DEFAULT) - Text("PrezelDividerType.THICK") - PrezelHorizontalDivider(type = PrezelDividerType.THICK) - - Spacer(modifier = Modifier.height(20.dp)) +private fun PrezelHorizontalDividerPreview() { + PreviewSection( + title = "PrezelDivider - Horizontal", + description = "Divider는 두 요소 사이를 구분합니다.", + ) { + PreviewValueRow(name = "DEFAULT") { + Box(Modifier.width(100.dp)) { + PrezelHorizontalDivider(type = PrezelDividerType.DEFAULT) + } + } + PreviewValueRow(name = "THICK") { + Box(Modifier.width(100.dp)) { + PrezelHorizontalDivider(type = PrezelDividerType.THICK) + } + } + } +} - SectionTitle(title = "PrezelDivider - Vertical") - Text("PrezelDividerType.DEFAULT") - PrezelVerticalDivider(type = PrezelDividerType.DEFAULT, modifier = Modifier.height(100.dp)) - Text("PrezelDividerType.THICK") - PrezelVerticalDivider(type = PrezelDividerType.THICK, modifier = Modifier.height(100.dp)) +@BasicPreview +@Composable +private fun PrezelVerticalDividerPreview() { + PreviewSection( + title = "PrezelDivider - Vertical", + description = "Divider는 두 요소 사이를 구분합니다.", + ) { + PreviewValueRow(name = "DEFAULT", modifier = Modifier.height(100.dp)) { + PrezelVerticalDivider(type = PrezelDividerType.DEFAULT) + } + PreviewValueRow(name = "THICK", modifier = Modifier.height(100.dp)) { + PrezelVerticalDivider(type = PrezelDividerType.THICK) } } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt index 76dd9715..ab3fafc7 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt @@ -1,5 +1,6 @@ package com.team.prezel.core.designsystem.component +import androidx.annotation.DrawableRes import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -24,11 +25,11 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.component.snackbar.PrezelSnackbarHost -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.preview.BasicPreview import com.team.prezel.core.designsystem.theme.PrezelTheme @Composable @@ -56,7 +57,7 @@ fun PrezelNavigationBar( fun RowScope.PrezelNavigationBarItem( selected: Boolean, onClick: () -> Unit, - icon: IconSource, + @DrawableRes iconResId: Int, label: String, modifier: Modifier = Modifier, enabled: Boolean = true, @@ -67,8 +68,8 @@ fun RowScope.PrezelNavigationBarItem( onClick = onClick, icon = { Icon( - painter = icon.painter(), - contentDescription = icon.contentDescription(), + painter = painterResource(id = iconResId), + contentDescription = null, ) }, modifier = modifier, @@ -123,9 +124,9 @@ class PrezelNavigationScope internal constructor( private val rowScope: RowScope, ) { @Composable - fun item( + fun Item( selected: Boolean, - icon: IconSource, + @DrawableRes iconResId: Int, label: String, onClick: () -> Unit, modifier: Modifier = Modifier, @@ -135,7 +136,7 @@ class PrezelNavigationScope internal constructor( rowScope.PrezelNavigationBarItem( selected = selected, onClick = onClick, - icon = icon, + iconResId = iconResId, label = label, modifier = modifier, enabled = enabled, @@ -144,28 +145,22 @@ class PrezelNavigationScope internal constructor( } } -@ThemePreview +@BasicPreview @Composable private fun PrezelNavigationScaffoldPreview() { var selectedIndex by rememberSaveable { mutableIntStateOf(0) } val snackbarHostState = remember { SnackbarHostState() } - val items = listOf( - Triple(IconSource(resId = PrezelIcons.Blank), "Label", 0), - Triple(IconSource(resId = PrezelIcons.Blank), "Label", 1), - Triple(IconSource(resId = PrezelIcons.Blank), "Label", 2), - ) - PrezelTheme { PrezelNavigationScaffold( snackbarHostState = snackbarHostState, navigationItems = { - items.forEach { (icon, label, index) -> - item( + repeat(3) { index -> + Item( selected = selectedIndex == index, onClick = { selectedIndex = index }, - icon = icon, - label = label, + iconResId = PrezelIcons.Blank, + label = "Tab $index", ) } }, diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelRadio.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelRadio.kt index c46b7e45..377b817d 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelRadio.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelRadio.kt @@ -3,7 +3,6 @@ package com.team.prezel.core.designsystem.component import androidx.compose.foundation.layout.Row 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.size import androidx.compose.foundation.layout.width @@ -26,9 +25,9 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp 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.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewRow +import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.theme.PrezelTheme /** @@ -128,30 +127,25 @@ private fun PrezelRadioIcon( ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelRadioPreview() { var checked by remember { mutableStateOf(false) } - PrezelTheme { - PreviewScaffold { - SectionTitle("PrezelRadioSize.REGULAR") - Text("Checked: true") + PreviewSection( + title = "PrezelRadio", + description = "라디오 버튼은 목록에서 선택할 항목이 한 개 있을 때 사용됩니다.", + ) { + PreviewRow { PrezelRadio(checked = true, onCheckedChange = {}, size = PrezelRadioSize.REGULAR) - Text("Checked: false") PrezelRadio(checked = false, onCheckedChange = {}, size = PrezelRadioSize.REGULAR) - Text("PrezelRadio with text") - PrezelRadio(checked = checked, onCheckedChange = { checked = it }, text = "텍스트", size = PrezelRadioSize.REGULAR) - - Spacer(modifier = Modifier.height(20.dp)) + PrezelRadio(checked = checked, onCheckedChange = { checked = it }, text = "Label", size = PrezelRadioSize.REGULAR) + } - SectionTitle("PrezelRadioSize.LARGE") - Text("Checked: true") + PreviewRow { PrezelRadio(checked = true, onCheckedChange = {}, size = PrezelRadioSize.LARGE) - Text("Checked: false") PrezelRadio(checked = false, onCheckedChange = {}, size = PrezelRadioSize.LARGE) - Text("PrezelRadio with text") - PrezelRadio(checked = checked, onCheckedChange = { checked = it }, text = "텍스트", size = PrezelRadioSize.LARGE) + PrezelRadio(checked = checked, onCheckedChange = { checked = it }, text = "Label", size = PrezelRadioSize.LARGE) } } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelTabs.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelTabs.kt index 649d4090..1743c7db 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelTabs.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelTabs.kt @@ -3,6 +3,7 @@ package com.team.prezel.core.designsystem.component import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -20,7 +21,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.foundation.typography.PrezelTextStyles -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewDefaults +import com.team.prezel.core.designsystem.preview.PreviewScaffold import com.team.prezel.core.designsystem.theme.PrezelTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -150,58 +153,50 @@ private fun handleTabClick( } } -@ThemePreview +@BasicPreview @Composable private fun PrezelMediumTabPreview() { val tabs = persistentListOf("Label1", "Label2", "Label3") val pagerState = rememberPagerState(initialPage = 0) { tabs.size } - PrezelTheme { - Box( - modifier = Modifier - .fillMaxSize() - .background(PrezelTheme.colors.bgRegular), - ) { - PrezelTabs( - tabs = tabs, - pagerState = pagerState, - size = PrezelTabSize.Regular, - modifier = Modifier, - ) { page -> - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center, - ) { - Text("Page: $page") - } + PreviewScaffold( + defaults = PreviewDefaults(screenPadding = PaddingValues(0.dp)), + ) { + PrezelTabs( + tabs = tabs, + pagerState = pagerState, + size = PrezelTabSize.Regular, + modifier = Modifier, + ) { page -> + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text("Page: $page") } } } } -@ThemePreview +@BasicPreview @Composable private fun PrezelSmallTabPreview() { val tabs = persistentListOf("Label1", "Label2") val pagerState = rememberPagerState(initialPage = 0) { tabs.size } - PrezelTheme { - Box( - modifier = Modifier - .fillMaxSize() - .background(PrezelTheme.colors.bgRegular), - ) { - PrezelTabs( - tabs = tabs, - pagerState = pagerState, - size = PrezelTabSize.Small, - ) { page -> - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center, - ) { - Text("Page: $page") - } + PreviewScaffold( + defaults = PreviewDefaults(screenPadding = PaddingValues(0.dp)), + ) { + PrezelTabs( + tabs = tabs, + pagerState = pagerState, + size = PrezelTabSize.Small, + ) { page -> + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text("Page: $page") } } } 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 b21c07f3..e8309fd8 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,15 +1,10 @@ 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 import androidx.compose.material3.ExperimentalMaterial3Api 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 @@ -17,12 +12,11 @@ import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -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.icon.PrezelIcons -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.theme.PrezelTheme @OptIn(ExperimentalMaterial3Api::class) @@ -60,105 +54,62 @@ private fun prezelTopAppBarColors() = ) @OptIn(ExperimentalMaterial3Api::class) -@ThemePreview +@BasicPreview @Composable private fun PrezelTopAppBarTitleOnlyPreview() { - PrezelTheme { - Surface(color = PrezelTheme.colors.bgRegular) { - PrezelTopAppBar(title = { Text(text = "제목") }) - } + PreviewSection(title = "Title Only") { + PrezelTopAppBar(title = { Text(text = "제목") }) } } @OptIn(ExperimentalMaterial3Api::class) -@ThemePreview +@BasicPreview @Composable private fun PrezelTopAppBarWithLeadingPreview() { - PrezelTheme { - Surface(color = PrezelTheme.colors.bgRegular) { - PrezelTopAppBar( - title = { Text(text = "제목") }, - leadingIcon = { - IconButton(onClick = {}) { - Icon( - painter = painterResource(PrezelIcons.Blank), - contentDescription = "뒤로가기", - ) - } - }, - ) - } + PreviewSection(title = "With Leading") { + PrezelTopAppBar( + title = { Text(text = "제목") }, + leadingIcon = { + IconButton(onClick = {}) { + Icon( + painter = painterResource(PrezelIcons.Blank), + contentDescription = "뒤로가기", + ) + } + }, + ) } } @OptIn(ExperimentalMaterial3Api::class) -@ThemePreview +@BasicPreview @Composable private fun PrezelTopAppBarWithAllIconsPreview() { - PrezelTheme { - Surface(color = PrezelTheme.colors.bgRegular) { - PrezelTopAppBar( - title = { Text(text = "Title") }, - 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 = "메뉴", - ) - } - }, - ) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@ThemePreview -@Composable -private fun PrezelTopAppBarScrollTestPreview() { - PrezelTheme { - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - - Scaffold( - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), - containerColor = PrezelTheme.colors.bgRegular, - topBar = { - PrezelTopAppBar( - title = { Text("Title") }, - leadingIcon = { - IconButton(onClick = {}) { - Icon( - painter = painterResource(PrezelIcons.Blank), - contentDescription = "뒤로가기", - ) - } - }, - scrollBehavior = scrollBehavior, - ) + PreviewSection(title = "With All Icons") { + PrezelTopAppBar( + title = { Text(text = "Title") }, + leadingIcon = { + IconButton(onClick = {}) { + Icon( + painter = painterResource(PrezelIcons.Blank), + contentDescription = "뒤로가기", + ) + } }, - ) { innerPadding -> - LazyColumn( - modifier = Modifier - .padding(innerPadding) - .padding(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp), - ) { - items(30) { index -> Text(text = "Item $index") } - } - } + trailingIcons = { + IconButton(onClick = {}) { + Icon( + painter = painterResource(PrezelIcons.Blank), + contentDescription = "검색", + ) + } + IconButton(onClick = {}) { + Icon( + painter = painterResource(PrezelIcons.Blank), + contentDescription = "메뉴", + ) + } + }, + ) } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/area/ButtonAreaScope.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/area/ButtonAreaScope.kt new file mode 100644 index 00000000..2774b134 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/area/ButtonAreaScope.kt @@ -0,0 +1,116 @@ +package com.team.prezel.core.designsystem.component.actions.area + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.LayoutScopeMarker +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.team.prezel.core.designsystem.component.actions.button.PrezelButton +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonHierarchy +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonType +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonDefault + +/** + * [PrezelButtonArea] 안에서 액션 버튼을 선언할 때 사용하는 scope입니다. + * + * `MainButton`을 먼저 선언하고 이어서 보조 버튼 하나를 추가하는 구성을 전제로 합니다. + */ +@LayoutScopeMarker +@Suppress("FunctionName") +interface ButtonAreaScope { + /** 주요 액션 버튼을 추가합니다. */ + fun MainButton( + @DrawableRes iconResId: Int? = null, + label: String, + enabled: Boolean = true, + onClick: () -> Unit, + ) + + /** 보조 액션 버튼을 추가합니다. */ + fun SubButton( + @DrawableRes iconResId: Int? = null, + label: String, + enabled: Boolean = true, + onClick: () -> Unit, + ) + + /** 버튼 프리셋을 직접 지정해 커스텀 액션 버튼을 추가합니다. */ + fun CustomButton( + @DrawableRes iconResId: Int? = null, + label: String, + enabled: Boolean, + onClick: () -> Unit, + config: PrezelButtonDefault, + ) +} + +internal class DefaultButtonAreaScope : ButtonAreaScope { + private val _buttons = mutableListOf<@Composable (Modifier) -> Unit>() + internal val buttons: List<@Composable (Modifier) -> Unit> get() = _buttons.toList() + + override fun MainButton( + @DrawableRes iconResId: Int?, + label: String, + enabled: Boolean, + onClick: () -> Unit, + ) { + validateButtonCount() + + _buttons += { modifier -> + PrezelButton( + modifier = modifier, + text = label, + iconResId = iconResId, + onClick = onClick, + enabled = enabled, + type = ButtonType.FILLED, + hierarchy = ButtonHierarchy.PRIMARY, + ) + } + } + + override fun SubButton( + @DrawableRes iconResId: Int?, + label: String, + enabled: Boolean, + onClick: () -> Unit, + ) { + validateButtonCount() + + _buttons += { modifier -> + PrezelButton( + modifier = modifier, + text = label, + iconResId = iconResId, + onClick = onClick, + enabled = enabled, + type = ButtonType.FILLED, + hierarchy = ButtonHierarchy.SECONDARY, + ) + } + } + + override fun CustomButton( + @DrawableRes iconResId: Int?, + label: String, + enabled: Boolean, + onClick: () -> Unit, + config: PrezelButtonDefault, + ) { + validateButtonCount() + + _buttons += { modifier -> + PrezelButton( + modifier = modifier, + text = label, + iconResId = iconResId, + onClick = onClick, + enabled = enabled, + config = config, + ) + } + } + + private fun validateButtonCount() { + require(_buttons.size < 2) { "버튼은 최대 2개까지 선언할 수 있습니다." } + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/area/PrezelButtonArea.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/area/PrezelButtonArea.kt new file mode 100644 index 00000000..cb398934 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/area/PrezelButtonArea.kt @@ -0,0 +1,303 @@ +package com.team.prezel.core.designsystem.component.actions.area + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +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.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.component.PrezelDividerType +import com.team.prezel.core.designsystem.component.PrezelHorizontalDivider +import com.team.prezel.core.designsystem.icon.PrezelIcons +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.theme.PrezelTheme + +/** + * 하단 액션 영역에 주요 버튼과 보조 버튼을 함께 배치합니다. + * + * `content`에서는 `MainButton`을 먼저 선언하고 이어서 보조 버튼 하나를 추가합니다. + * `isVertical`과 `showBackground` 조합에 따라 배치와 기본 스타일이 함께 달라집니다. + */ +@Composable +fun PrezelButtonArea( + modifier: Modifier = Modifier, + isVertical: Boolean = true, + isStrongStrength: Boolean = true, + showBackground: Boolean = false, + isNested: Boolean = false, + config: PrezelButtonAreaDefault = PrezelButtonAreaDefaults.getDefault(), + content: @Composable ButtonAreaScope.() -> Unit, +) { + val scope = remember { DefaultButtonAreaScope() } + scope.content() + + val mainButton = requireNotNull(scope.buttons.firstOrNull()) { + "PrezelButtonArea에는 최소 1개의 버튼이 필요합니다." + } + val subButton = scope.buttons.getOrNull(1) + val contentModifier = if (isNested) Modifier else Modifier.padding(config.contentPadding) + + Column( + modifier = modifier + .fillMaxWidth() + .then(if (showBackground) Modifier.background(config.backgroundColor) else Modifier), + ) { + if (showBackground) { + PrezelHorizontalDivider(type = PrezelDividerType.THICK, color = config.borderColor) + } + + if (isVertical) { + ButtonAreaVertical( + mainButton = mainButton, + subButton = subButton, + modifier = contentModifier, + ) + } else { + ButtonAreaHorizontal( + isStrongStrength = isStrongStrength, + mainButton = mainButton, + subButton = subButton, + modifier = contentModifier, + ) + } + } +} + +@Composable +private fun ButtonAreaVertical( + mainButton: @Composable (Modifier) -> Unit, + subButton: @Composable ((Modifier) -> Unit)?, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier) { + mainButton(Modifier.fillMaxWidth()) + + if (subButton != null) { + Spacer(modifier = Modifier.height(PrezelTheme.spacing.V12)) + subButton(Modifier.fillMaxWidth()) + } + } +} + +@Composable +private fun ButtonAreaHorizontal( + isStrongStrength: Boolean, + mainButton: @Composable (Modifier) -> Unit, + subButton: @Composable ((Modifier) -> Unit)?, + modifier: Modifier = Modifier, +) { + Row(modifier = modifier) { + if (subButton != null) { + subButton(if (isStrongStrength) Modifier else Modifier.weight(1f)) + Spacer(modifier = Modifier.width(PrezelTheme.spacing.V12)) + } + + mainButton(Modifier.weight(1f)) + } +} + +private data class ButtonAreaPreviewVariant( + val isVertical: Boolean, + val isStrongStrength: Boolean, +) + +private val buttonAreaPreviewStates = + listOf( + true to "Enabled", + false to "Disabled", + ) + +@Preview(device = "spec:width=1080dp,height=1800dp") +@Composable +private fun PrezelButtonAreaVerticalPreview() { + PreviewSection( + title = "Button Area - Vertical", + description = "행은 버튼 상태, 열은 배경 여부입니다. 각 섹션은 배치 방향과 강도 옵션을 구분합니다.", + ) { + Column { + ButtonAreaPreviewHeaderRow() + + buttonAreaPreviewStates.forEach { (enabled, label) -> + ButtonAreaPreviewRow( + variant = ButtonAreaPreviewVariant( + isVertical = true, + isStrongStrength = false, + ), + enabled = enabled, + label = label, + ) + } + } + } +} + +@Preview(device = "spec:width=1080dp,height=1800dp") +@Composable +private fun PrezelButtonAreaHorizontalPreview() { + PreviewSection( + title = "Button Area - Horizontal", + description = "행은 버튼 상태, 열은 배경 여부입니다. 각 섹션은 배치 방향과 강도 옵션을 구분합니다.", + ) { + Column { + ButtonAreaPreviewHeaderRow() + + buttonAreaPreviewStates.forEach { (enabled, label) -> + ButtonAreaPreviewRow( + variant = ButtonAreaPreviewVariant( + isVertical = false, + isStrongStrength = true, + ), + enabled = enabled, + label = label, + ) + ButtonAreaPreviewRow( + variant = ButtonAreaPreviewVariant( + isVertical = false, + isStrongStrength = false, + ), + enabled = enabled, + label = label, + ) + } + } + } +} + +@Composable +private fun ButtonAreaPreviewHeaderRow() { + Row( + modifier = Modifier.height(IntrinsicSize.Min), + horizontalArrangement = Arrangement.spacedBy(0.dp), + ) { + ButtonAreaPreviewHeaderCell( + text = "State", + modifier = Modifier.weight(0.5f), + ) + ButtonAreaPreviewHeaderCell( + text = "No Background", + modifier = Modifier.weight(1f), + ) + ButtonAreaPreviewHeaderCell( + text = "Background", + modifier = Modifier.weight(1f), + ) + } +} + +@Composable +private fun ButtonAreaPreviewRow( + variant: ButtonAreaPreviewVariant, + enabled: Boolean, + label: String, +) { + Row( + modifier = Modifier.height(IntrinsicSize.Min), + horizontalArrangement = Arrangement.spacedBy(0.dp), + ) { + ButtonAreaPreviewCell(modifier = Modifier.weight(0.5f)) { + Text( + text = label, + style = PrezelTheme.typography.body3Medium, + color = PrezelTheme.colors.textLarge, + textAlign = TextAlign.Center, + ) + } + + ButtonAreaPreviewCell(modifier = Modifier.weight(1f)) { + ButtonAreaPreviewSample( + variant = variant, + enabled = enabled, + showBackground = false, + ) + } + + ButtonAreaPreviewCell(modifier = Modifier.weight(1f)) { + ButtonAreaPreviewSample( + variant = variant, + enabled = enabled, + showBackground = true, + ) + } + } +} + +@Composable +private fun ButtonAreaPreviewSample( + variant: ButtonAreaPreviewVariant, + enabled: Boolean, + showBackground: Boolean, +) { + PrezelButtonArea( + modifier = Modifier.fillMaxWidth(), + isVertical = variant.isVertical, + isStrongStrength = variant.isStrongStrength, + showBackground = showBackground, + ) { + MainButton( + iconResId = PrezelIcons.Blank, + label = "Label", + enabled = enabled, + onClick = {}, + ) + + SubButton( + iconResId = PrezelIcons.Blank, + label = "Label", + enabled = enabled, + onClick = {}, + ) + } +} + +@Composable +private fun ButtonAreaPreviewHeaderCell( + text: String, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .fillMaxHeight() + .border(width = PrezelTheme.stroke.V1, color = PrezelTheme.colors.borderRegular) + .background(PrezelTheme.colors.bgMedium) + .padding(horizontal = 12.dp, vertical = 10.dp), + contentAlignment = Alignment.Center, + ) { + Text( + text = text, + style = PrezelTheme.typography.body3Medium, + color = PrezelTheme.colors.textMedium, + textAlign = TextAlign.Center, + ) + } +} + +@Composable +private fun ButtonAreaPreviewCell( + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) { + Box( + modifier = modifier + .fillMaxHeight() + .border(width = PrezelTheme.stroke.V1, color = PrezelTheme.colors.borderRegular) + .padding(horizontal = 12.dp, vertical = 16.dp), + contentAlignment = Alignment.Center, + ) { + content() + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/area/PrezelButtonAreaDefaults.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/area/PrezelButtonAreaDefaults.kt new file mode 100644 index 00000000..42fa6a9d --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/area/PrezelButtonAreaDefaults.kt @@ -0,0 +1,46 @@ +package com.team.prezel.core.designsystem.component.actions.area + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +import com.team.prezel.core.designsystem.theme.PrezelTheme + +/** + * 버튼 영역의 배경, 구분선, 내부 여백을 묶은 스타일 값입니다. + */ +@Immutable +data class PrezelButtonAreaDefault( + val backgroundColor: Color, + val borderColor: Color, + val contentPadding: PaddingValues, +) + +/** + * [PrezelButtonArea]의 기본 스타일 값을 계산합니다. + */ +object PrezelButtonAreaDefaults { + /** + * 버튼 영역에 사용할 기본 스타일을 반환합니다. + */ + @Composable + fun getDefault( + backgroundColor: Color = getBackgroundColor(), + borderColor: Color = getBorderColor(), + contentPadding: PaddingValues = getContentPadding(), + ): PrezelButtonAreaDefault = + PrezelButtonAreaDefault( + backgroundColor = backgroundColor, + borderColor = borderColor, + contentPadding = contentPadding, + ) + + @Composable + private fun getBackgroundColor(): Color = PrezelTheme.colors.bgRegular + + @Composable + private fun getBorderColor(): Color = PrezelTheme.colors.borderRegular + + @Composable + private fun getContentPadding(): PaddingValues = PaddingValues(PrezelTheme.spacing.V20) +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelButton.kt new file mode 100644 index 00000000..6370c2cb --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelButton.kt @@ -0,0 +1,71 @@ +package com.team.prezel.core.designsystem.component.actions.button + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonHierarchy +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonSize +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonType +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonBase +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonDefault +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonDefaults +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonPreviewContent +import com.team.prezel.core.designsystem.component.actions.button.config.previewGhostBorder +import com.team.prezel.core.designsystem.icon.PrezelIcons + +/** + * 아이콘과 텍스트를 함께 표시할 수 있는 기본 버튼입니다. + */ +@Composable +fun PrezelButton( + text: String, + modifier: Modifier = Modifier, + @DrawableRes iconResId: Int? = null, + type: ButtonType = ButtonType.FILLED, + size: ButtonSize = ButtonSize.REGULAR, + hierarchy: ButtonHierarchy = ButtonHierarchy.PRIMARY, + enabled: Boolean = true, + isRounded: Boolean = false, + config: PrezelButtonDefault = PrezelButtonDefaults.getDefault( + isIconOnly = false, + isRounded = isRounded, + type = type, + size = size, + hierarchy = hierarchy, + ), + onClick: () -> Unit, +) { + PrezelButtonBase( + text = text, + iconResId = iconResId, + enabled = enabled, + onClick = onClick, + modifier = modifier, + config = config, + ) +} + +@Preview(device = "spec:width=1080dp,height=1400dp") +@Composable +private fun PrezelButtonPreview() { + PrezelButtonPreviewContent(title = "Button/Icon + Text") { type, hierarchy, size, enabled, isRounded -> + PrezelButton( + text = "Label", + iconResId = PrezelIcons.Blank, + type = type, + size = size, + hierarchy = hierarchy, + enabled = enabled, + isRounded = isRounded, + onClick = {}, + modifier = Modifier.previewGhostBorder( + type = type, + hierarchy = hierarchy, + size = size, + isRounded = isRounded, + isIconOnly = false, + ), + ) + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelHyperlinkButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelHyperlinkButton.kt new file mode 100644 index 00000000..f3ee475d --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelHyperlinkButton.kt @@ -0,0 +1,77 @@ +package com.team.prezel.core.designsystem.component.actions.button + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonHierarchy +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonSize +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonType +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonBase +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonDefault +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonDefaults +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.theme.PrezelTheme +import com.team.prezel.core.designsystem.util.drawDashBorder + +/** + * 텍스트 링크처럼 보이는 보조 액션 버튼입니다. + */ +@Composable +fun PrezelHyperlinkButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + size: ButtonSize = ButtonSize.XSMALL, + config: PrezelButtonDefault = PrezelButtonDefaults.getDefault( + isIconOnly = false, + isRounded = false, + size = size, + type = ButtonType.GHOST, + hierarchy = ButtonHierarchy.SECONDARY, + ), +) { + PrezelButtonBase( + text = text, + iconResId = null, + enabled = true, + onClick = onClick, + modifier = modifier, + layoutModifier = Modifier.prezelButtonUnderline(), + config = config, + ) +} + +@Composable +private fun Modifier.prezelButtonUnderline(): Modifier { + val underlineThickness = with(LocalDensity.current) { 1.dp.toPx() } + val underlineColor = PrezelTheme.colors.borderLarge + + return this.drawBehind { + val y = size.height - (underlineThickness / 2) + drawLine( + color = underlineColor, + start = Offset(0f, y), + end = Offset(size.width, y), + strokeWidth = underlineThickness, + ) + } +} + +@BasicPreview +@Composable +private fun PrezelHyperlinkButtonPreview() { + PreviewSection( + title = "Hyperlink Button", + description = "텍스트 링크처럼 보이는 보조 액션 버튼입니다.", + ) { + PrezelHyperlinkButton( + text = "자세히 보기", + onClick = {}, + modifier = Modifier.drawDashBorder(), + ) + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelIconButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelIconButton.kt new file mode 100644 index 00000000..8ff33b72 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelIconButton.kt @@ -0,0 +1,69 @@ +package com.team.prezel.core.designsystem.component.actions.button + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonHierarchy +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonSize +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonType +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonBase +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonDefault +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonDefaults +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonPreviewContent +import com.team.prezel.core.designsystem.component.actions.button.config.previewGhostBorder +import com.team.prezel.core.designsystem.icon.PrezelIcons + +/** + * 아이콘만 노출하는 액션 버튼입니다. + */ +@Composable +fun PrezelIconButton( + @DrawableRes iconResId: Int, + modifier: Modifier = Modifier, + type: ButtonType = ButtonType.FILLED, + size: ButtonSize = ButtonSize.REGULAR, + hierarchy: ButtonHierarchy = ButtonHierarchy.PRIMARY, + enabled: Boolean = true, + isRounded: Boolean = false, + buttonDefault: PrezelButtonDefault = PrezelButtonDefaults.getDefault( + isIconOnly = true, + isRounded = isRounded, + type = type, + size = size, + hierarchy = hierarchy, + ), + onClick: () -> Unit, +) { + PrezelButtonBase( + text = null, + iconResId = iconResId, + enabled = enabled, + onClick = onClick, + modifier = modifier, + config = buttonDefault, + ) +} + +@Preview(device = "spec:width=1080dp,height=1500dp") +@Composable +private fun PrezelIconButtonPreview() { + PrezelButtonPreviewContent(title = "Button/Icon") { type, hierarchy, size, enabled, isRounded -> + PrezelIconButton( + iconResId = PrezelIcons.Blank, + type = type, + size = size, + hierarchy = hierarchy, + enabled = enabled, + isRounded = isRounded, + onClick = {}, + modifier = Modifier.previewGhostBorder( + type = type, + hierarchy = hierarchy, + size = size, + isRounded = isRounded, + isIconOnly = true, + ), + ) + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelTextButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelTextButton.kt new file mode 100644 index 00000000..6aa31137 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/PrezelTextButton.kt @@ -0,0 +1,67 @@ +package com.team.prezel.core.designsystem.component.actions.button + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonHierarchy +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonSize +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonType +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonBase +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonDefault +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonDefaults +import com.team.prezel.core.designsystem.component.actions.button.config.PrezelButtonPreviewContent +import com.team.prezel.core.designsystem.component.actions.button.config.previewGhostBorder + +/** + * 텍스트만 표시하는 버튼입니다. + */ +@Composable +fun PrezelTextButton( + text: String, + modifier: Modifier = Modifier, + type: ButtonType = ButtonType.FILLED, + size: ButtonSize = ButtonSize.REGULAR, + hierarchy: ButtonHierarchy = ButtonHierarchy.PRIMARY, + enabled: Boolean = true, + isRounded: Boolean = false, + config: PrezelButtonDefault = PrezelButtonDefaults.getDefault( + isIconOnly = false, + isRounded = isRounded, + type = type, + size = size, + hierarchy = hierarchy, + ), + onClick: () -> Unit, +) { + PrezelButtonBase( + text = text, + iconResId = null, + onClick = onClick, + enabled = enabled, + modifier = modifier, + config = config, + ) +} + +@Preview(device = "spec:width=1080dp,height=1350dp") +@Composable +private fun PrezelTextButtonPreview() { + PrezelButtonPreviewContent(title = "Button/Text") { type, hierarchy, size, enabled, isRounded -> + PrezelTextButton( + text = "Label", + type = type, + size = size, + hierarchy = hierarchy, + enabled = enabled, + isRounded = isRounded, + onClick = {}, + modifier = Modifier.previewGhostBorder( + type = type, + hierarchy = hierarchy, + size = size, + isRounded = isRounded, + isIconOnly = false, + ), + ) + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonBase.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonBase.kt new file mode 100644 index 00000000..1fc2ab9f --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonBase.kt @@ -0,0 +1,147 @@ +package com.team.prezel.core.designsystem.component.actions.button.config + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import com.team.prezel.core.designsystem.component.base.PrezelTouchArea + +/** + * 버튼 계열 컴포넌트가 공통으로 사용하는 실제 렌더링 구현입니다. + */ +@Composable +internal fun PrezelButtonBase( + text: String?, + @DrawableRes iconResId: Int?, + enabled: Boolean, + onClick: () -> Unit, + config: PrezelButtonDefault, + modifier: Modifier = Modifier, + layoutModifier: Modifier = Modifier, + isUseRipple: Boolean = true, +) { + PrezelTouchArea( + modifier = modifier.buttonContainer(enabled = enabled, config = config), + extraTouchPadding = config.contentPadding, + onClick = onClick, + enabled = enabled, + shape = config.shape, + isUseRipple = isUseRipple, + ) { + ButtonContentLayout( + modifier = layoutModifier, + horizontalArrangement = config.contentArrangement( + hasText = text != null, + hasIcon = iconResId != null, + ), + text = text?.let { buttonText -> + { + ButtonLabel( + text = buttonText, + enabled = enabled, + config = config, + ) + } + }, + leadingIcon = iconResId?.let { drawableRes -> + { + ButtonLeadingIcon( + drawableRes = drawableRes, + enabled = enabled, + config = config, + ) + } + }, + ) + } +} + +@Composable +private fun ButtonContentLayout( + modifier: Modifier, + horizontalArrangement: Arrangement.Horizontal, + text: @Composable (() -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, +) { + Box( + modifier = modifier, + contentAlignment = Alignment.Center, + ) { + Row( + horizontalArrangement = horizontalArrangement, + verticalAlignment = Alignment.CenterVertically, + ) { + leadingIcon?.invoke() + text?.invoke() + } + } +} + +@Composable +private fun ButtonLabel( + text: String, + enabled: Boolean, + config: PrezelButtonDefault, +) { + Text( + text = text, + style = config.textStyle, + color = config.contentColor(enabled = enabled), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.Center, + ) +} + +@Composable +private fun ButtonLeadingIcon( + @DrawableRes drawableRes: Int, + enabled: Boolean, + config: PrezelButtonDefault, +) { + Image( + modifier = Modifier.size(config.iconSize), + painter = painterResource(id = drawableRes), + contentScale = ContentScale.FillHeight, + colorFilter = ColorFilter.tint(color = config.contentColor(enabled = enabled)), + contentDescription = null, + ) +} + +private fun PrezelButtonDefault.contentArrangement( + hasText: Boolean, + hasIcon: Boolean, +): Arrangement.Horizontal = if (hasText && hasIcon) Arrangement.spacedBy(iconSpacing) else Arrangement.Center + +private fun Modifier.buttonContainer( + enabled: Boolean, + config: PrezelButtonDefault, +): Modifier = + this + .clip(shape = config.shape) + .background(color = config.backgroundColor(enabled = enabled)) + .then( + if (config.hasBorder) { + Modifier.border( + width = config.borderWidth, + color = config.borderColor(enabled = enabled), + shape = config.shape, + ) + } else { + Modifier + }, + ) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonDefaults.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonDefaults.kt new file mode 100644 index 00000000..391a5498 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonDefaults.kt @@ -0,0 +1,218 @@ +package com.team.prezel.core.designsystem.component.actions.button.config + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +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.PrezelColorScheme +import com.team.prezel.core.designsystem.theme.PrezelTheme + +/** + * 버튼 컴포넌트가 공통으로 사용하는 시각 속성 묶음입니다. + */ +@Immutable +data class PrezelButtonDefault( + private val contentColor: Color, + private val disabledContentColor: Color, + private val backgroundColor: Color, + private val disabledBackgroundColor: Color, + private val borderColor: Color, + private val disabledBorderColor: Color, + val borderWidth: Dp, + val shape: RoundedCornerShape, + val textStyle: TextStyle, + val contentPadding: PaddingValues, + val iconSpacing: Dp, + val iconSize: Dp, +) { + val hasBorder: Boolean = borderWidth > 0.dp + + fun contentColor(enabled: Boolean): Color = if (enabled) contentColor else disabledContentColor + + fun backgroundColor(enabled: Boolean): Color = if (enabled) backgroundColor else disabledBackgroundColor + + fun borderColor(enabled: Boolean): Color = if (enabled) borderColor else disabledBorderColor +} + +/** + * 버튼 타입과 크기에 맞는 기본 스타일 값을 제공합니다. + */ +object PrezelButtonDefaults { + /** + * 버튼 종류와 사용 형태에 맞는 기본 스타일을 계산합니다. + * + * 커스텀 `config`를 만들 때 기준값으로 사용할 수 있습니다. + */ + @Composable + fun getDefault( + isIconOnly: Boolean, + type: ButtonType, + size: ButtonSize, + hierarchy: ButtonHierarchy, + isRounded: Boolean, + contentColor: Color = getContentColor(type = type, hierarchy = hierarchy), + disabledContentColor: Color = getDisabledContentColor(), + backgroundColor: Color = getBackgroundColor(type = type, hierarchy = hierarchy), + disabledBackgroundColor: Color = getDisabledBackgroundColor(type = type), + borderColor: Color = getBorderColor(type = type, hierarchy = hierarchy), + disabledBorderColor: Color = getDisabledBorderColor(type = type), + borderWidth: Dp = getBorderWidth(type = type), + shape: RoundedCornerShape = getShape(isRounded = isRounded, isIconOnly = isIconOnly, size = size), + textStyle: TextStyle = getTextStyle(size = size), + contentPadding: PaddingValues = getContentPadding(size = size, isIconOnly = isIconOnly), + iconSpacing: Dp = getIconSpacing(size = size), + iconSize: Dp = getIconSize(size = size), + ) = PrezelButtonDefault( + contentColor = contentColor, + disabledContentColor = disabledContentColor, + backgroundColor = backgroundColor, + disabledBackgroundColor = disabledBackgroundColor, + borderColor = borderColor, + disabledBorderColor = disabledBorderColor, + borderWidth = borderWidth, + shape = shape, + textStyle = textStyle, + contentPadding = contentPadding, + iconSpacing = iconSpacing, + iconSize = iconSize, + ) + + @Composable + private fun getContentColor( + type: ButtonType, + hierarchy: ButtonHierarchy, + ): Color { + if (hierarchy == ButtonHierarchy.SECONDARY) return PrezelTheme.colors.textMedium + + return when (type) { + ButtonType.FILLED -> PrezelColorScheme.Dark.textLarge + ButtonType.OUTLINED, + ButtonType.GHOST, + -> PrezelTheme.colors.interactiveRegular + } + } + + @Composable + private fun getDisabledContentColor(): Color = PrezelTheme.colors.textDisabled + + @Composable + private fun getBackgroundColor( + type: ButtonType, + hierarchy: ButtonHierarchy, + ): Color = + when (type) { + ButtonType.OUTLINED, + ButtonType.GHOST, + -> Color.Transparent + + ButtonType.FILLED -> { + if (hierarchy == ButtonHierarchy.SECONDARY) PrezelTheme.colors.bgLarge else PrezelTheme.colors.interactiveRegular + } + } + + @Composable + private fun getDisabledBackgroundColor(type: ButtonType): Color = + when (type) { + ButtonType.OUTLINED, + ButtonType.GHOST, + -> Color.Transparent + + ButtonType.FILLED -> PrezelTheme.colors.bgLarge + } + + @Composable + private fun getBorderColor( + type: ButtonType, + hierarchy: ButtonHierarchy, + ): Color { + if (type != ButtonType.OUTLINED) return Color.Transparent + + return when (hierarchy) { + ButtonHierarchy.PRIMARY -> PrezelTheme.colors.interactiveRegular + ButtonHierarchy.SECONDARY -> PrezelTheme.colors.borderMedium + } + } + + @Composable + private fun getDisabledBorderColor(type: ButtonType): Color { + if (type != ButtonType.OUTLINED) return Color.Transparent + + return PrezelTheme.colors.borderDisabled + } + + @Composable + private fun getBorderWidth(type: ButtonType): Dp = if (type == ButtonType.OUTLINED) PrezelTheme.stroke.V1 else 0.dp + + @Composable + private fun getShape( + isRounded: Boolean, + isIconOnly: Boolean, + size: ButtonSize, + ): RoundedCornerShape { + if (isRounded) return PrezelTheme.shapes.V1000 + + return when (size) { + ButtonSize.REGULAR -> PrezelTheme.shapes.V8 + ButtonSize.SMALL -> if (isIconOnly) PrezelTheme.shapes.V6 else PrezelTheme.shapes.V4 + ButtonSize.XSMALL -> PrezelTheme.shapes.V4 + } + } + + @Composable + private fun getTextStyle(size: ButtonSize): TextStyle = + when (size) { + ButtonSize.XSMALL -> PrezelTheme.typography.caption2Medium + ButtonSize.SMALL -> PrezelTheme.typography.body3Medium + ButtonSize.REGULAR -> PrezelTheme.typography.body2Bold + } + + @Composable + private fun getContentPadding( + size: ButtonSize, + isIconOnly: Boolean, + ): PaddingValues { + if (isIconOnly) { + val all = when (size) { + ButtonSize.XSMALL -> PrezelTheme.spacing.V8 + ButtonSize.SMALL -> PrezelTheme.spacing.V10 + ButtonSize.REGULAR -> PrezelTheme.spacing.V14 + } + + return PaddingValues(all = all) + } + + val horizontal = when (size) { + ButtonSize.XSMALL -> PrezelTheme.spacing.V10 + ButtonSize.SMALL -> PrezelTheme.spacing.V12 + ButtonSize.REGULAR -> PrezelTheme.spacing.V16 + } + val vertical = when (size) { + ButtonSize.XSMALL -> PrezelTheme.spacing.V6 + ButtonSize.SMALL -> PrezelTheme.spacing.V8 + ButtonSize.REGULAR -> PrezelTheme.spacing.V12 + } + + return PaddingValues(horizontal = horizontal, vertical = vertical) + } + + @Composable + private fun getIconSpacing(size: ButtonSize): Dp = + when (size) { + ButtonSize.XSMALL, + ButtonSize.SMALL, + -> PrezelTheme.spacing.V4 + + ButtonSize.REGULAR -> PrezelTheme.spacing.V8 + } + + private fun getIconSize(size: ButtonSize): Dp = + when (size) { + ButtonSize.XSMALL -> 14.dp + ButtonSize.SMALL -> 16.dp + ButtonSize.REGULAR -> 20.dp + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonModels.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonModels.kt new file mode 100644 index 00000000..4a47aa34 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonModels.kt @@ -0,0 +1,51 @@ +package com.team.prezel.core.designsystem.component.actions.button.config + +import androidx.compose.runtime.Immutable + +/** + * 버튼의 시각적 타입을 정의합니다. + * + * 버튼의 배경, 테두리, 강조 방식에 따라 스타일을 구분할 때 사용합니다. + */ +@Immutable +enum class ButtonType { + /** 배경이 채워진 버튼입니다. */ + FILLED, + + /** 테두리가 있는 버튼입니다. */ + OUTLINED, + + /** 배경과 테두리가 없는 버튼입니다. */ + GHOST, +} + +/** + * 버튼의 크기를 정의합니다. + * + * 버튼의 높이, 패딩, 아이콘 크기, 텍스트 스타일 등의 기준으로 사용합니다. + */ +@Immutable +enum class ButtonSize { + /** 가장 작은 크기의 버튼입니다. */ + XSMALL, + + /** 작은 크기의 버튼입니다. */ + SMALL, + + /** 기본 크기의 버튼입니다. */ + REGULAR, +} + +/** + * 버튼의 계층 구조를 정의합니다. + * + * 액션의 중요도와 강조 수준에 따라 버튼의 우선순위를 표현할 때 사용합니다. + */ +@Immutable +enum class ButtonHierarchy { + /** 가장 중요한 주요 액션에 사용되는 계층입니다. */ + PRIMARY, + + /** 보조 액션에 사용되는 계층입니다. */ + SECONDARY, +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonPreview.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonPreview.kt new file mode 100644 index 00000000..0bc0279a --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonPreview.kt @@ -0,0 +1,199 @@ +package com.team.prezel.core.designsystem.component.actions.button.config + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.theme.PrezelTheme +import com.team.prezel.core.designsystem.util.drawDashBorder + +private data class ButtonPreviewGroup( + val title: String, + val enabled: Boolean, + val isRounded: Boolean, +) + +private val previewSections = + listOf( + ButtonPreviewGroup(title = "Enabled / Default", enabled = true, isRounded = false), + ButtonPreviewGroup(title = "Enabled / Rounded", enabled = true, isRounded = true), + ButtonPreviewGroup(title = "Disabled / Default", enabled = false, isRounded = false), + ButtonPreviewGroup(title = "Disabled / Rounded", enabled = false, isRounded = true), + ) + +@Composable +internal fun PrezelButtonPreviewContent( + title: String, + content: @Composable (ButtonType, ButtonHierarchy, ButtonSize, Boolean, Boolean) -> Unit, +) { + PreviewSection( + title = title, + description = "행은 Size, 열은 Type과 Hierarchy 조합입니다. 각 섹션은 Enabled와 Rounded 상태를 구분합니다.", + ) { + previewSections.forEach { section -> + ButtonPreviewSection( + title = section.title, + enabled = section.enabled, + isRounded = section.isRounded, + content = content, + ) + } + } +} + +@Composable +internal fun Modifier.previewGhostBorder( + type: ButtonType, + hierarchy: ButtonHierarchy, + size: ButtonSize, + isRounded: Boolean, + isIconOnly: Boolean, +): Modifier { + if (type != ButtonType.GHOST) return this + + return this.drawDashBorder( + shape = PrezelButtonDefaults + .getDefault( + isIconOnly = isIconOnly, + isRounded = isRounded, + type = type, + size = size, + hierarchy = hierarchy, + ).shape, + ) +} + +@Composable +private fun ButtonPreviewSection( + title: String, + enabled: Boolean, + isRounded: Boolean, + content: @Composable (ButtonType, ButtonHierarchy, ButtonSize, Boolean, Boolean) -> Unit, +) { + Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { + Text( + text = title, + style = PrezelTheme.typography.body3Bold, + color = PrezelTheme.colors.textLarge, + ) + + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(0.dp), + ) { + ButtonPreviewHeaderRow() + + ButtonSize.entries.forEach { size -> + ButtonPreviewRow( + size = size, + enabled = enabled, + isRounded = isRounded, + content = content, + ) + } + } + } +} + +@Composable +private fun ButtonPreviewHeaderRow() { + Row( + modifier = Modifier.height(IntrinsicSize.Min), + horizontalArrangement = Arrangement.spacedBy(0.dp), + ) { + ButtonPreviewHeaderCell( + text = "Size", + modifier = Modifier.weight(1f), + ) + + ButtonType.entries.forEach { type -> + ButtonHierarchy.entries.forEach { hierarchy -> + ButtonPreviewHeaderCell( + text = "${type.name}\n${hierarchy.name}", + modifier = Modifier.weight(1f), + ) + } + } + } +} + +@Composable +private fun ButtonPreviewRow( + size: ButtonSize, + enabled: Boolean, + isRounded: Boolean, + content: @Composable (ButtonType, ButtonHierarchy, ButtonSize, Boolean, Boolean) -> Unit, +) { + Row( + modifier = Modifier.height(IntrinsicSize.Min), + horizontalArrangement = Arrangement.spacedBy(0.dp), + ) { + ButtonPreviewCell(modifier = Modifier.weight(1f)) { + Text( + text = size.name, + style = PrezelTheme.typography.body3Medium, + color = PrezelTheme.colors.textLarge, + ) + } + + ButtonType.entries.forEach { type -> + ButtonHierarchy.entries.forEach { hierarchy -> + ButtonPreviewCell(modifier = Modifier.weight(1f)) { + content(type, hierarchy, size, enabled, isRounded) + } + } + } + } +} + +@Composable +private fun ButtonPreviewHeaderCell( + text: String, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .fillMaxHeight() + .border(width = PrezelTheme.stroke.V1, color = PrezelTheme.colors.borderRegular) + .background(PrezelTheme.colors.bgMedium) + .padding(horizontal = 12.dp, vertical = 10.dp), + contentAlignment = Alignment.Center, + ) { + Text( + text = text, + style = PrezelTheme.typography.body3Medium, + color = PrezelTheme.colors.textMedium, + textAlign = TextAlign.Center, + ) + } +} + +@Composable +private fun ButtonPreviewCell( + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) { + Box( + modifier = modifier + .fillMaxHeight() + .border(width = PrezelTheme.stroke.V1, color = PrezelTheme.colors.borderRegular) + .padding(horizontal = 12.dp, vertical = 16.dp), + contentAlignment = Alignment.Center, + ) { + content() + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingButton.kt new file mode 100644 index 00000000..5dcc2047 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingButton.kt @@ -0,0 +1,74 @@ +package com.team.prezel.core.designsystem.component.actions.button.floating + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import com.team.prezel.core.designsystem.component.actions.button.PrezelIconButton +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonHierarchy +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonSize +import com.team.prezel.core.designsystem.component.base.PrezelDropShadowDefaults +import com.team.prezel.core.designsystem.component.base.prezelDropShadow +import com.team.prezel.core.designsystem.icon.PrezelIcons +import com.team.prezel.core.designsystem.preview.BasicPreview +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 PrezelFloatingButton( + isExpanded: Boolean, + onChangeExpanded: (Boolean) -> Unit, + @DrawableRes iconResId: Int, + modifier: Modifier = Modifier, + size: ButtonSize = ButtonSize.REGULAR, + hierarchy: ButtonHierarchy = ButtonHierarchy.PRIMARY, + @DrawableRes openIconResId: Int = PrezelIcons.Cancel, +) { + PrezelIconButton( + iconResId = if (isExpanded) openIconResId else iconResId, + size = size, + hierarchy = hierarchy, + isRounded = true, + modifier = modifier.prezelDropShadow( + style = PrezelDropShadowDefaults.Regular( + borderRadius = PrezelTheme.radius.V1000, + ), + ), + onClick = { onChangeExpanded(!isExpanded) }, + ) +} + +@BasicPreview +@Composable +private fun PrezelFloatingButtonPreview() { + var expanded by remember { mutableStateOf(false) } + + PreviewSection( + title = "Floating Button", + description = "Floating Button은 아이콘으로 공통 기능을 안내합니다.", + ) { + ButtonHierarchy.entries.forEach { hierarchy -> + ButtonSize.entries.forEach { size -> + PreviewValueRow( + name = hierarchy.name, + valueLabel = size.name, + ) { + PrezelFloatingButton( + isExpanded = expanded, + onChangeExpanded = { expanded = it }, + iconResId = PrezelIcons.Blank, + hierarchy = hierarchy, + size = size, + ) + } + } + } + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingMenu.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingMenu.kt new file mode 100644 index 00000000..1bcc5066 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingMenu.kt @@ -0,0 +1,103 @@ +package com.team.prezel.core.designsystem.component.actions.button.floating + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonHierarchy +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonSize +import com.team.prezel.core.designsystem.component.actions.button.floating.menu.MenuSize +import com.team.prezel.core.designsystem.component.actions.button.floating.menu.PrezelMenu +import com.team.prezel.core.designsystem.component.actions.button.floating.menu.PrezelMenuScope +import com.team.prezel.core.designsystem.icon.PrezelIcons +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.theme.PrezelTheme + +/** + * 확장 가능한 메뉴와 토글 버튼을 함께 배치하는 플로팅 액션 메뉴입니다. + */ +@Composable +fun PrezelFloatingMenu( + isExpanded: Boolean, + onChangeExpanded: (Boolean) -> Unit, + @DrawableRes iconResId: Int, + modifier: Modifier = Modifier, + @DrawableRes openIconResId: Int = PrezelIcons.Cancel, + size: ButtonSize = ButtonSize.REGULAR, + hierarchy: ButtonHierarchy = ButtonHierarchy.PRIMARY, + items: @Composable PrezelMenuScope.() -> Unit, +) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.End, + ) { + if (isExpanded) { + PrezelMenu( + size = when (size) { + ButtonSize.REGULAR -> MenuSize.REGULAR + ButtonSize.SMALL, + ButtonSize.XSMALL, + -> MenuSize.SMALL + }, + content = items, + ) + Spacer(modifier = Modifier.height(PrezelTheme.spacing.V16)) + } + + PrezelFloatingButton( + isExpanded = isExpanded, + onChangeExpanded = onChangeExpanded, + iconResId = iconResId, + openIconResId = openIconResId, + size = size, + hierarchy = hierarchy, + ) + } +} + +@BasicPreview +@Composable +private fun PrezelFloatingMenuPreview() { + var expanded by remember { mutableStateOf(true) } + + PreviewSection( + title = "Floating Menu", + description = "확장된 메뉴와 토글 버튼의 배치를 확인합니다.", + ) { + Box( + modifier = Modifier + .background(Color.LightGray) + .padding(8.dp), + ) { + PrezelFloatingMenu( + isExpanded = expanded, + onChangeExpanded = { expanded = it }, + iconResId = PrezelIcons.Blank, + hierarchy = ButtonHierarchy.PRIMARY, + size = ButtonSize.REGULAR, + modifier = Modifier.align(Alignment.BottomEnd), + ) { + repeat(5) { + MenuItem( + label = "Label", + iconResId = PrezelIcons.Blank, + onClick = { expanded = !expanded }, + ) + } + } + } + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenu.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenu.kt new file mode 100644 index 00000000..23228692 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenu.kt @@ -0,0 +1,66 @@ +package com.team.prezel.core.designsystem.component.actions.button.floating.menu + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.icon.PrezelIcons +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow + +/** + * 플로팅 액션 메뉴 항목을 세로로 배치하는 컨테이너입니다. + */ +@Composable +fun PrezelMenu( + modifier: Modifier = Modifier, + size: MenuSize = MenuSize.REGULAR, + config: PrezelMenuDefault = PrezelMenuDefaults.getDefault(size), + content: @Composable PrezelMenuScope.() -> Unit, +) { + val scope = remember(size) { DefaultPrezelMenuScope(menuSize = size) } + + Column( + modifier = modifier + .clip(shape = config.shape) + .background(color = config.backgroundColor) + .padding(config.contentPadding), + verticalArrangement = config.verticalArrangement, + ) { + scope.content() + } +} + +@BasicPreview +@Composable +private fun PrezelMenuPreview() { + PreviewSection( + title = "Floating Menu Container", + description = "Regular/Small 메뉴 컨테이너의 크기와 간격을 비교합니다.", + ) { + MenuSize.entries.forEach { size -> + PreviewValueRow(name = size.name) { + PrezelMenu( + size = size, + modifier = Modifier + .background(Color.LightGray) + .padding(8.dp), + ) { + repeat(3) { + MenuItem( + label = "Label", + iconResId = PrezelIcons.Blank, + onClick = {}, + ) + } + } + } + } + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuDefaults.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuDefaults.kt new file mode 100644 index 00000000..22cbcf17 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuDefaults.kt @@ -0,0 +1,61 @@ +package com.team.prezel.core.designsystem.component.actions.button.floating.menu + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +import com.team.prezel.core.designsystem.theme.PrezelTheme + +/** + * [PrezelMenu]의 크기 프리셋입니다. + */ +enum class MenuSize { + SMALL, + REGULAR, +} + +/** + * 메뉴 컨테이너의 패딩, 배경, shape를 묶은 스타일 값입니다. + */ +@Immutable +data class PrezelMenuDefault( + val contentPadding: PaddingValues, + val backgroundColor: Color, + val verticalArrangement: Arrangement.Vertical, + val shape: RoundedCornerShape, +) + +/** + * [PrezelMenu]의 기본 스타일 값을 제공합니다. + */ +object PrezelMenuDefaults { + /** 메뉴 크기에 맞는 기본 컨테이너 스타일을 반환합니다. */ + @Composable + fun getDefault(size: MenuSize): PrezelMenuDefault = + PrezelMenuDefault( + contentPadding = getContentPadding(size), + backgroundColor = getBackgroundColor(), + verticalArrangement = getVerticalArrangement(), + shape = getShape(), + ) + + @Composable + private fun getContentPadding(size: MenuSize): PaddingValues = + PaddingValues( + when (size) { + MenuSize.SMALL -> PrezelTheme.spacing.V4 + MenuSize.REGULAR -> PrezelTheme.spacing.V6 + }, + ) + + @Composable + private fun getBackgroundColor(): Color = PrezelTheme.colors.bgRegular + + @Composable + private fun getVerticalArrangement(): Arrangement.Vertical = Arrangement.spacedBy(PrezelTheme.spacing.V4) + + @Composable + private fun getShape(): RoundedCornerShape = PrezelTheme.shapes.V12 +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuItem.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuItem.kt new file mode 100644 index 00000000..a003e58b --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuItem.kt @@ -0,0 +1,99 @@ +package com.team.prezel.core.designsystem.component.actions.button.floating.menu + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Row +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.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import com.team.prezel.core.designsystem.component.base.PrezelTouchArea +import com.team.prezel.core.designsystem.icon.PrezelIcons +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow +import com.team.prezel.core.designsystem.util.drawDashBorder + +/** + * 메뉴 scope가 실제로 그리는 클릭 가능한 단일 메뉴 아이템입니다. + */ +@Composable +internal fun PrezelMenuItem( + label: String, + @DrawableRes iconResId: Int, + modifier: Modifier = Modifier, + size: MenuItemSize = MenuItemSize.REGULAR, + config: PrezelMenuItemDefault = PrezelMenuItemDefaults.getDefault(size), + onClick: () -> Unit, +) { + PrezelTouchArea( + modifier = modifier, + onClick = onClick, + shape = config.shape, + extraTouchPadding = config.contentPadding, + ) { + PrezelMenuItemLayout( + label = label, + iconResId = iconResId, + config = config, + ) + } +} + +@Composable +private fun PrezelMenuItemLayout( + label: String, + @DrawableRes iconResId: Int, + config: PrezelMenuItemDefault, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + painter = painterResource(id = iconResId), + contentDescription = null, + modifier = Modifier.size(config.iconSize), + tint = config.contentColor, + ) + + Spacer(modifier = Modifier.width(config.spacing)) + + Text( + text = label, + style = config.textStyle, + color = config.contentColor, + ) + } +} + +@BasicPreview +@Composable +private fun PrezelMenuMenuItemPreview() { + PreviewSection( + title = "Floating Menu Item", + description = "Floating Menu Item의 크기를 조절합니다.", + ) { + MenuSize.entries.forEach { size -> + PreviewValueRow(name = size.name) { + PrezelMenu( + size = size, + modifier = Modifier.drawDashBorder( + shape = PrezelMenuDefaults.getDefault(size = size).shape, + ), + ) { + MenuItem( + label = "Label", + iconResId = PrezelIcons.Blank, + onClick = {}, + ) + } + } + } + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuItemDefaults.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuItemDefaults.kt new file mode 100644 index 00000000..b16f488e --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuItemDefaults.kt @@ -0,0 +1,100 @@ +package com.team.prezel.core.designsystem.component.actions.button.floating.menu + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +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 + +/** + * [PrezelMenuItem]의 크기 프리셋입니다. + */ +enum class MenuItemSize { + SMALL, + REGULAR, +} + +/** + * 메뉴 아이템의 아이콘 크기, 간격, 타이포그래피를 묶은 스타일 값입니다. + */ +@Immutable +data class PrezelMenuItemDefault( + val iconSize: Dp, + val contentPadding: PaddingValues, + val spacing: Dp, + val textStyle: TextStyle, + val contentColor: Color, + val shape: RoundedCornerShape, +) + +/** + * 메뉴 아이템 크기에 맞는 기본 스타일 값을 제공합니다. + */ +object PrezelMenuItemDefaults { + /** 메뉴 아이템 크기에 맞는 기본 스타일을 반환합니다. */ + @Composable + fun getDefault(size: MenuItemSize): PrezelMenuItemDefault = + PrezelMenuItemDefault( + iconSize = getIconSize(size), + contentPadding = getContentPadding(size), + spacing = getSpacing(size), + textStyle = getTextStyle(size), + contentColor = getContentColor(), + shape = getShape(), + ) + + private fun getIconSize(size: MenuItemSize): Dp = + when (size) { + MenuItemSize.SMALL -> 16.dp + MenuItemSize.REGULAR -> 20.dp + } + + @Composable + private fun getContentPadding(size: MenuItemSize): PaddingValues { + val verticalPadding = when (size) { + MenuItemSize.SMALL -> PrezelTheme.spacing.V4 + MenuItemSize.REGULAR -> PrezelTheme.spacing.V8 + } + + val startPadding = when (size) { + MenuItemSize.SMALL -> PrezelTheme.spacing.V8 + MenuItemSize.REGULAR -> PrezelTheme.spacing.V12 + } + + val endPadding = when (size) { + MenuItemSize.SMALL -> PrezelTheme.spacing.V10 + MenuItemSize.REGULAR -> PrezelTheme.spacing.V16 + } + + return PaddingValues( + start = startPadding, + end = endPadding, + top = verticalPadding, + bottom = verticalPadding, + ) + } + + @Composable + private fun getSpacing(size: MenuItemSize): Dp = + when (size) { + MenuItemSize.SMALL -> PrezelTheme.spacing.V4 + MenuItemSize.REGULAR -> PrezelTheme.spacing.V8 + } + + @Composable + private fun getTextStyle(size: MenuItemSize): TextStyle = + when (size) { + MenuItemSize.SMALL -> PrezelTheme.typography.body3Regular + MenuItemSize.REGULAR -> PrezelTheme.typography.body2Regular + } + + @Composable + private fun getContentColor(): Color = PrezelTheme.colors.textMedium + + @Composable + private fun getShape(): RoundedCornerShape = PrezelTheme.shapes.V8 +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuScope.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuScope.kt new file mode 100644 index 00000000..76b9f130 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/menu/PrezelMenuScope.kt @@ -0,0 +1,45 @@ +package com.team.prezel.core.designsystem.component.actions.button.floating.menu + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.LayoutScopeMarker +import androidx.compose.runtime.Composable + +/** + * [PrezelMenu] 안에서 메뉴 항목을 선언할 때 사용하는 scope입니다. + */ +@LayoutScopeMarker +interface PrezelMenuScope { + val menuSize: MenuSize + val itemSize: MenuItemSize + + /** 현재 메뉴 크기에 맞는 기본 메뉴 아이템을 추가합니다. */ + @Composable + fun MenuItem( + label: String, + @DrawableRes iconResId: Int, + onClick: () -> Unit, + ) +} + +internal class DefaultPrezelMenuScope( + override val menuSize: MenuSize, +) : PrezelMenuScope { + override val itemSize: MenuItemSize = when (menuSize) { + MenuSize.SMALL -> MenuItemSize.SMALL + MenuSize.REGULAR -> MenuItemSize.REGULAR + } + + @Composable + override fun MenuItem( + label: String, + @DrawableRes iconResId: Int, + onClick: () -> Unit, + ) { + PrezelMenuItem( + label = label, + iconResId = iconResId, + size = itemSize, + onClick = onClick, + ) + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/base/PrezelDropShadow.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/base/PrezelDropShadow.kt new file mode 100644 index 00000000..b8b24ad2 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/base/PrezelDropShadow.kt @@ -0,0 +1,344 @@ +package com.team.prezel.core.designsystem.component.base + +import android.graphics.BlurMaskFilter +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.RoundRect +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Canvas +import androidx.compose.ui.graphics.ClipOp +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Paint +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.drawIntoCanvas +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow + +/** + * 디자인 시스템 그림자 토큰을 적용하고 같은 모서리 반경의 배경을 함께 그립니다. + */ +@Composable +fun Modifier.prezelDropShadow(style: PrezelDropShadowDefaults.PrezelShadowStyle): Modifier { + val cachedShadows = remember(style) { style.getShadow() } + + return this + .dropShadow( + shadows = cachedShadows, + borderRadius = style.borderRadius, + isBackgroundTransparent = style.backgroundColor == Color.Transparent, + ).background( + color = style.backgroundColor, + shape = RoundedCornerShape(style.borderRadius), + ) +} + +/** + * 여러 개의 shadow 토큰을 기반으로 컴포넌트 외곽에 그림자를 그립니다. + */ +private fun Modifier.dropShadow( + shadows: List, + borderRadius: Dp, + isBackgroundTransparent: Boolean = false, +): Modifier = + drawWithCache { + if (shadows.isEmpty()) { + return@drawWithCache onDrawBehind {} + } + + val borderRadiusPx = borderRadius.toPx() + val paint = Paint() + val frameworkPaint = paint.asFrameworkPaint() + val clipPath = createShadowClipPath( + isBackgroundTransparent = isBackgroundTransparent, + borderRadiusPx = borderRadiusPx, + width = size.width, + height = size.height, + ) + val resolvedShadows = toResolvedShadows(shadows = shadows, size = size) + + onDrawBehind { + drawShadows( + shadows = resolvedShadows, + paint = paint, + frameworkPaint = frameworkPaint, + clipPath = clipPath, + borderRadiusPx = borderRadiusPx, + isBackgroundTransparent = isBackgroundTransparent, + ) + } + } + +/** + * 투명 배경에서 내부 영역을 제외하기 위한 clip path를 생성합니다. + */ +private fun createShadowClipPath( + isBackgroundTransparent: Boolean, + borderRadiusPx: Float, + width: Float, + height: Float, +): Path? { + if (!isBackgroundTransparent || borderRadiusPx <= 0f) return null + + return Path().apply { + addRoundRect( + roundRect = RoundRect( + left = 0f, + top = 0f, + right = width, + bottom = height, + radiusX = borderRadiusPx, + radiusY = borderRadiusPx, + ), + ) + } +} + +/** + * shadow 토큰 목록을 실제 draw에 사용할 값으로 변환합니다. + */ +private fun Density.toResolvedShadows( + shadows: List, + size: Size, +): List { + val maskFiltersByBlurRadius = mapBlurMaskFilters(shadows) + + return shadows.map { shadow -> + val spreadRadiusPx = shadow.spreadRadius.toPx() + val offsetXPx = shadow.offsetX.toPx() + val offsetYPx = shadow.offsetY.toPx() + val blurRadiusPx = shadow.blurRadius.toPx() + + ResolvedShadow( + color = shadow.color.toArgb(), + left = -spreadRadiusPx + offsetXPx, + top = -spreadRadiusPx + offsetYPx, + right = size.width + spreadRadiusPx + offsetXPx, + bottom = size.height + spreadRadiusPx + offsetYPx, + maskFilter = maskFiltersByBlurRadius.getValue(blurRadiusPx), + ) + } +} + +/** + * blur 반경별로 재사용 가능한 mask filter를 생성합니다. + */ +private fun Density.mapBlurMaskFilters(shadows: List): Map = + shadows + .map { it.blurRadius.toPx() } + .distinct() + .associateWith { blurRadiusPx -> + if (blurRadiusPx <= 0f) { + null + } else { + BlurMaskFilter(blurRadiusPx, BlurMaskFilter.Blur.NORMAL) + } + } + +/** + * 준비된 shadow 목록을 뒤쪽 레이어부터 순서대로 그립니다. + */ +private fun DrawScope.drawShadows( + shadows: List, + paint: Paint, + frameworkPaint: android.graphics.Paint, + clipPath: Path?, + borderRadiusPx: Float, + isBackgroundTransparent: Boolean, +) { + drawIntoCanvas { drawCanvas -> + for (index in shadows.indices.reversed()) { + val shadow = shadows[index] + frameworkPaint.color = shadow.color + frameworkPaint.maskFilter = shadow.maskFilter + + drawCanvas.drawShadowLayer( + shadow = shadow, + paint = paint, + clipPath = clipPath, + borderRadiusPx = borderRadiusPx, + width = size.width, + height = size.height, + isBackgroundTransparent = isBackgroundTransparent, + ) + } + + frameworkPaint.maskFilter = null + } +} + +/** + * 단일 shadow 레이어를 그리기 전에 clip을 적용하고 실제 도형을 렌더링합니다. + */ +private fun Canvas.drawShadowLayer( + shadow: ResolvedShadow, + paint: Paint, + clipPath: Path?, + borderRadiusPx: Float, + width: Float, + height: Float, + isBackgroundTransparent: Boolean, +) { + applyTransparentClip( + clipPath = clipPath, + borderRadiusPx = borderRadiusPx, + width = width, + height = height, + isBackgroundTransparent = isBackgroundTransparent, + ) + + if (borderRadiusPx > 0f) { + drawRoundRect( + left = shadow.left, + top = shadow.top, + right = shadow.right, + bottom = shadow.bottom, + radiusX = borderRadiusPx, + radiusY = borderRadiusPx, + paint = paint, + ) + } else { + drawRect( + left = shadow.left, + top = shadow.top, + right = shadow.right, + bottom = shadow.bottom, + paint = paint, + ) + } + + if (isBackgroundTransparent) { + restore() + } +} + +/** + * 투명 배경인 경우 내부 영역을 제외하도록 clip을 적용합니다. + */ +private fun Canvas.applyTransparentClip( + clipPath: Path?, + borderRadiusPx: Float, + width: Float, + height: Float, + isBackgroundTransparent: Boolean, +) { + if (!isBackgroundTransparent) return + + save() + + if (borderRadiusPx > 0f) { + clipPath?.let { path -> + clipPath(path, ClipOp.Difference) + } + return + } + + clipRect( + left = 0f, + top = 0f, + right = width, + bottom = height, + clipOp = ClipOp.Difference, + ) +} + +/** + * shadow 하나를 draw 가능한 좌표와 paint 정보로 보관합니다. + */ +private data class ResolvedShadow( + val color: Int, + val left: Float, + val top: Float, + val right: Float, + val bottom: Float, + val maskFilter: BlurMaskFilter?, +) + +/** + * [prezelDropShadow]에 전달할 그림자 스타일 프리셋을 제공합니다. + */ +object PrezelDropShadowDefaults { + /** + * 그림자 레이어와 배경 처리를 함께 정의하는 스타일 계약입니다. + */ + sealed class PrezelShadowStyle( + open val borderRadius: Dp, + open val backgroundColor: Color, + ) { + abstract fun getShadow(): List + } + + /** 그림자를 그리지 않는 스타일입니다. */ + data object None : PrezelShadowStyle( + borderRadius = 0.dp, + backgroundColor = Color.Transparent, + ) { + override fun getShadow(): List = emptyList() + } + + /** 기본 elevation 느낌의 단일 그림자를 적용하는 스타일입니다. */ + data class Regular( + override val borderRadius: Dp = 0.dp, + override val backgroundColor: Color = Color.Transparent, + ) : PrezelShadowStyle(borderRadius, backgroundColor) { + private val shadowList by lazy { + listOf( + PrezelShadowToken( + offsetX = 0.dp, + offsetY = 2.dp, + blurRadius = 4.dp, + spreadRadius = 0.dp, + color = Color(0x1F000713), + ), + ) + } + + override fun getShadow(): List = shadowList + } + + /** + * 개별 그림자 레이어의 오프셋, blur, spread, 색상을 정의합니다. + */ + data class PrezelShadowToken( + val offsetX: Dp, + val offsetY: Dp, + val blurRadius: Dp, + val spreadRadius: Dp, + val color: Color, + ) +} + +@BasicPreview +@Composable +private fun PrezelDropShadowPreview() { + val styles = listOf( + PrezelDropShadowDefaults.None, + PrezelDropShadowDefaults.Regular(), + ) + + PreviewSection(title = "Drop Shadow") { + styles.forEach { style -> + PreviewValueRow( + name = style.javaClass.simpleName, + valueLabel = "", + ) { + Box( + modifier = Modifier + .size(80.dp) + .prezelDropShadow(style = style), + ) + } + } + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/base/PrezelTouchArea.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/base/PrezelTouchArea.kt new file mode 100644 index 00000000..8ec69b5e --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/base/PrezelTouchArea.kt @@ -0,0 +1,89 @@ +package com.team.prezel.core.designsystem.component.base + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.ripple +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow +import com.team.prezel.core.designsystem.util.drawDashBorder + +/** + * 시각 크기보다 넓은 터치 여유 영역을 줄 수 있는 클릭 컨테이너입니다. + * + * `extraTouchPadding`으로 터치 영역을 확보하고 필요할 때 ripple 표시를 끌 수 있습니다. + */ +@Composable +fun PrezelTouchArea( + modifier: Modifier = Modifier, + extraTouchPadding: PaddingValues = PaddingValues(0.dp), + shape: Shape = RectangleShape, + enabled: Boolean = true, + rippleColor: Color = Color.Unspecified, + isUseRipple: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + onClick: () -> Unit, + content: @Composable BoxScope.() -> Unit, +) { + Box( + modifier = modifier + .clip(shape = shape) + .clickable( + enabled = enabled, + onClick = onClick, + indication = if (isUseRipple) ripple(color = rippleColor) else null, + interactionSource = interactionSource, + ).padding(extraTouchPadding), + contentAlignment = Alignment.Center, + ) { + content() + } +} + +@BasicPreview +@Composable +private fun PrezelTouchAreaPreview() { + PreviewSection( + title = "Touch Area", + description = "시각 크기와 별도로 터치 가능한 영역을 확인합니다.", + ) { + PreviewValueRow(name = "Extra Touch Padding (0.dp)") { + PrezelTouchArea(onClick = {}) { + Box( + modifier = Modifier + .size(48.dp) + .background(Color.Cyan), + ) + } + } + + PreviewValueRow(name = "Extra Touch Padding (12.dp)") { + PrezelTouchArea( + onClick = {}, + extraTouchPadding = PaddingValues(12.dp), + modifier = Modifier.drawDashBorder(), + ) { + Box( + modifier = Modifier + .size(48.dp) + .background(Color.Cyan), + ) + } + } + } +} 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 deleted file mode 100644 index acd2cbf4..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButton.kt +++ /dev/null @@ -1,151 +0,0 @@ -package com.team.prezel.core.designsystem.component.button - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -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.Text -import androidx.compose.material3.ripple -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.unit.dp -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 PrezelButton( - onClick: () -> Unit, - modifier: Modifier = Modifier, - text: String? = null, - icon: IconSource? = null, - enabled: Boolean = true, - style: PrezelButtonStyle = PrezelButtonStyle(), -) { - require(text != null || icon != null) { "버튼은 텍스트 또는 아이콘 중 하나는 반드시 필요합니다." } - val appearance = PrezelButtonAppearance.of(style = style, isIconOnly = text == null, enabled = enabled) - - Box( - modifier = modifier - .applyButtonAppearance(appearance) - .clickable( - enabled = enabled, - onClick = onClick, - role = Role.Button, - indication = ripple(), - interactionSource = null, - ), - contentAlignment = Alignment.Center, - ) { - PrezelButtonContent( - text = text, - icon = icon, - showUnderline = style.showUnderline, - appearance = appearance, - ) - } -} - -@Composable -private fun PrezelButtonContent( - text: String?, - icon: IconSource?, - showUnderline: Boolean, - appearance: PrezelButtonAppearance, -) { - CompositionLocalProvider(LocalContentColor provides appearance.contentColor) { - Row( - modifier = Modifier.padding(appearance.contentPadding), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - icon?.let { source -> - Icon( - painter = source.painter(), - contentDescription = source.contentDescription(), - modifier = Modifier.size(appearance.iconSize), - ) - } - - text?.let { label -> - if (icon != null) { - Spacer(modifier = Modifier.width(width = appearance.iconSpacing)) - } - Text( - text = label, - style = appearance.textStyle, - modifier = if (showUnderline) Modifier.prezelButtonUnderline() else Modifier, - ) - } - } - } -} - -@Composable -private fun Modifier.prezelButtonUnderline(): Modifier { - val underlineThickness = with(LocalDensity.current) { 1.dp.toPx() } - val underlineColor = PrezelTheme.colors.borderLarge - - return this.drawBehind { - val y = size.height - (underlineThickness / 2) - drawLine( - color = underlineColor, - start = Offset(0f, y), - end = Offset(size.width, y), - strokeWidth = underlineThickness, - ) - } -} - -@ThemePreview -@Composable -private fun PrezelButtonPreviewFilled() { - PrezelTheme { - PrezelButtonPreviewByType(type = PrezelButtonType.FILLED, content = ::PrezelButtonPreviewItem) - } -} - -@ThemePreview -@Composable -private fun PrezelButtonPreviewOutlined() { - PrezelTheme { - PrezelButtonPreviewByType(type = PrezelButtonType.OUTLINED, content = ::PrezelButtonPreviewItem) - } -} - -@ThemePreview -@Composable -private fun PrezelButtonPreviewGhost() { - PrezelTheme { - PrezelButtonPreviewByType(type = PrezelButtonType.GHOST, content = ::PrezelButtonPreviewItem) - } -} - -@Composable -private fun PrezelButtonPreviewItem( - style: PrezelButtonStyle, - enabled: Boolean, - modifier: Modifier = Modifier, -) { - PrezelButton( - text = "Label", - icon = IconSource(resId = PrezelIcons.Blank), - onClick = {}, - enabled = enabled, - style = style, - modifier = modifier, - ) -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonArea.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonArea.kt deleted file mode 100644 index cd94db1b..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonArea.kt +++ /dev/null @@ -1,190 +0,0 @@ -package com.team.prezel.core.designsystem.component.button - -import androidx.compose.foundation.background -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.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -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.unit.dp -import com.team.prezel.core.designsystem.component.PrezelDividerType -import com.team.prezel.core.designsystem.component.PrezelHorizontalDivider -import com.team.prezel.core.designsystem.icon.IconSource -import com.team.prezel.core.designsystem.icon.PrezelIcons -import com.team.prezel.core.designsystem.preview.SectionTitle -import com.team.prezel.core.designsystem.preview.ThemePreview -import com.team.prezel.core.designsystem.theme.PrezelTheme - -@Immutable -data class ButtonAreaButtonSpec( - val icon: IconSource? = null, - val label: String, - val enabled: Boolean = true, - val onClick: () -> Unit, -) - -@Composable -fun PrezelButtonArea( - mainButton: ButtonAreaButtonSpec, - subButton: ButtonAreaButtonSpec?, - modifier: Modifier = Modifier, - isVertical: Boolean = true, - showBackground: Boolean = false, - isStrongStrength: Boolean = true, - contentPadding: PaddingValues = PaddingValues(PrezelTheme.spacing.V20), -) { - Column(modifier = modifier) { - if (showBackground) PrezelHorizontalDivider(type = PrezelDividerType.THICK) - - val contentModifier = Modifier - .background(if (showBackground) PrezelTheme.colors.bgRegular else Color.Transparent) - .padding(contentPadding) - .fillMaxWidth() - - if (isVertical) { - ButtonAreaVertical( - modifier = contentModifier, - mainButton = mainButton, - subButton = subButton, - ) - } else { - ButtonAreaHorizontal( - modifier = contentModifier, - isStrongStrength = isStrongStrength, - mainButton = mainButton, - subButton = subButton, - ) - } - } -} - -@Composable -private fun ButtonAreaVertical( - modifier: Modifier, - mainButton: ButtonAreaButtonSpec, - subButton: ButtonAreaButtonSpec?, -) { - Column(modifier = modifier) { - PrezelButton( - modifier = Modifier.fillMaxWidth(), - text = mainButton.label, - icon = mainButton.icon, - onClick = mainButton.onClick, - enabled = mainButton.enabled, - style = PrezelButtonStyle(buttonHierarchy = PrezelButtonHierarchy.PRIMARY), - ) - - if (subButton != null) { - Spacer(modifier = Modifier.height(PrezelTheme.spacing.V12)) - PrezelButton( - modifier = Modifier.fillMaxWidth(), - text = subButton.label, - icon = subButton.icon, - onClick = subButton.onClick, - enabled = subButton.enabled, - style = PrezelButtonStyle(buttonHierarchy = PrezelButtonHierarchy.SECONDARY), - ) - } - } -} - -@Composable -private fun ButtonAreaHorizontal( - modifier: Modifier, - isStrongStrength: Boolean, - mainButton: ButtonAreaButtonSpec, - subButton: ButtonAreaButtonSpec?, -) { - Row(modifier = modifier) { - val mainModifier = Modifier.weight(1f) - val subModifier = if (isStrongStrength) Modifier else Modifier.weight(1f) - - if (subButton != null) { - PrezelButton( - modifier = subModifier, - text = subButton.label, - icon = subButton.icon, - onClick = subButton.onClick, - enabled = subButton.enabled, - style = PrezelButtonStyle(buttonHierarchy = PrezelButtonHierarchy.SECONDARY), - ) - Spacer(modifier = Modifier.width(PrezelTheme.spacing.V12)) - } - - PrezelButton( - modifier = mainModifier, - text = mainButton.label, - icon = mainButton.icon, - onClick = mainButton.onClick, - enabled = mainButton.enabled, - style = PrezelButtonStyle(buttonHierarchy = PrezelButtonHierarchy.PRIMARY), - ) - } -} - -@ThemePreview -@Composable -private fun PrezelButtonAreaVerticalPreview() { - PrezelButtonAreaTypedPreview(title = "Vertical", isVertical = true, isStrongStrength = true) -} - -@ThemePreview -@Composable -private fun PrezelButtonAreaHorizontalStrongPreview() { - PrezelButtonAreaTypedPreview(title = "Horizontal & Strong Strength", isVertical = false, isStrongStrength = true) -} - -@ThemePreview -@Composable -private fun PrezelButtonAreaHorizontalWeakPreview() { - PrezelButtonAreaTypedPreview(title = "Horizontal & Weak Strength", isVertical = false, isStrongStrength = false) -} - -@Composable -private fun PrezelButtonAreaTypedPreview( - title: String, - isVertical: Boolean, - isStrongStrength: Boolean, -) { - val mainButton = ButtonAreaButtonSpec( - label = "Main Button", - icon = IconSource(resId = PrezelIcons.Blank), - onClick = {}, - ) - val subButton = ButtonAreaButtonSpec( - label = "Sub", - icon = IconSource(resId = PrezelIcons.Blank), - onClick = {}, - ) - - PrezelTheme { - Column( - modifier = Modifier.background(PrezelTheme.colors.bgMedium), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - SectionTitle(title = title) - PrezelButtonArea( - isVertical = isVertical, - isStrongStrength = isStrongStrength, - showBackground = false, - mainButton = mainButton, - subButton = subButton, - ) - PrezelButtonArea( - isVertical = isVertical, - isStrongStrength = isStrongStrength, - showBackground = true, - mainButton = mainButton, - subButton = subButton, - ) - } - } -} 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 deleted file mode 100644 index 70d4fa73..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonPreview.kt +++ /dev/null @@ -1,127 +0,0 @@ -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.Alignment -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 com.team.prezel.core.designsystem.util.drawDashBorder -import kotlinx.collections.immutable.persistentListOf - -internal typealias PrezelButtonPreviewContent = @Composable (style: PrezelButtonStyle, enabled: Boolean, contentModifier: Modifier) -> 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), -) - -@Composable -internal fun PrezelButtonPreviewByType( - type: PrezelButtonType, - content: PrezelButtonPreviewContent, - isIconOnly: Boolean = false, -) { - PreviewScaffold { - Text(text = type.name, style = PrezelTheme.typography.title2Medium) - - PreviewVariants.forEach { variant -> - HorizontalDivider() - PrezelButtonVariantSection( - type = type, - enabled = variant.enabled, - isRounded = variant.isRounded, - isIconOnly = isIconOnly, - content = content, - ) - } - } -} - -@Composable -private fun PrezelButtonVariantSection( - type: PrezelButtonType, - enabled: Boolean, - isRounded: Boolean, - isIconOnly: Boolean, - content: PrezelButtonPreviewContent, - modifier: Modifier = Modifier, - contentModifier: 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, - isIconOnly = isIconOnly, - content = content, - contentModifier = contentModifier, - ) - Text(text = "Hierarchy: Secondary | Enabled: $enabled | Radius: $isRounded", style = PrezelTheme.typography.body3Medium) - PrezelButtonPreviewHierarchyBlock( - type = type, - hierarchy = PrezelButtonHierarchy.SECONDARY, - enabled = enabled, - isRounded = isRounded, - isIconOnly = isIconOnly, - content = content, - contentModifier = contentModifier, - ) - } -} - -@Composable -private fun PrezelButtonPreviewHierarchyBlock( - type: PrezelButtonType, - hierarchy: PrezelButtonHierarchy, - enabled: Boolean, - isRounded: Boolean, - isIconOnly: Boolean, - content: PrezelButtonPreviewContent, - modifier: Modifier = Modifier, - contentModifier: Modifier = Modifier, -) { - Row( - modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - PrezelButtonSize.entries.forEach { size -> - val style = PrezelButtonStyle( - buttonType = type, - buttonHierarchy = hierarchy, - buttonSize = size, - isRounded = isRounded, - ) - val appearance = PrezelButtonAppearance.of(style = style, isIconOnly = isIconOnly, enabled = enabled) - - content( - style, - enabled, - contentModifier - .then( - if (type == PrezelButtonType.GHOST) Modifier.drawDashBorder(shape = appearance.shape) else Modifier, - ), - ) - } - } -} 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 deleted file mode 100644 index 2f5d73aa..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelButtonStyle.kt +++ /dev/null @@ -1,243 +0,0 @@ -package com.team.prezel.core.designsystem.component.button - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -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.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, -} - -@Immutable -data class PrezelButtonStyle( - val buttonType: PrezelButtonType = PrezelButtonType.FILLED, - val buttonHierarchy: PrezelButtonHierarchy = PrezelButtonHierarchy.PRIMARY, - val buttonSize: PrezelButtonSize = PrezelButtonSize.REGULAR, - val isRounded: Boolean = false, - val showUnderline: Boolean = false, -) - -@Immutable -internal data class PrezelButtonAppearance( - val textStyle: TextStyle, - val contentColor: Color, - val contentPadding: PaddingValues, - val iconSpacing: Dp, - val shape: Shape, - val containerColor: Color, - val borderStroke: BorderStroke, - val iconSize: Dp, -) { - companion object { - @Composable - fun of( - style: PrezelButtonStyle, - isIconOnly: Boolean, - enabled: Boolean, - ): PrezelButtonAppearance = - PrezelButtonAppearance( - textStyle = prezelButtonTextStyle(size = style.buttonSize), - contentColor = prezelButtonContentColor( - type = style.buttonType, - hierarchy = style.buttonHierarchy, - enabled = enabled, - ), - contentPadding = prezelButtonContentPadding( - size = style.buttonSize, - isIconOnly = isIconOnly, - ), - iconSpacing = prezelButtonIconSpacing(size = style.buttonSize), - shape = prezelButtonShape( - isIconOnly = isIconOnly, - isRounded = style.isRounded, - buttonSize = style.buttonSize, - ), - containerColor = prezelButtonContainerColor( - type = style.buttonType, - hierarchy = style.buttonHierarchy, - enabled = enabled, - ), - borderStroke = prezelButtonBorderStroke( - type = style.buttonType, - hierarchy = style.buttonHierarchy, - enabled = enabled, - ), - iconSize = prezelButtonIconSize(size = style.buttonSize), - ) - } -} - -internal fun Modifier.applyButtonAppearance(appearance: PrezelButtonAppearance): Modifier = - this - .clip(appearance.shape) - .background(appearance.containerColor) - .border( - border = appearance.borderStroke, - shape = appearance.shape, - ) - -@Composable -private fun prezelButtonIconSpacing( - size: PrezelButtonSize, - spacing: PrezelSpacing = PrezelTheme.spacing, -): Dp = - when (size) { - PrezelButtonSize.XSMALL -> spacing.V4 - PrezelButtonSize.SMALL -> spacing.V4 - PrezelButtonSize.REGULAR -> spacing.V8 - } - -@Composable -private fun prezelButtonShape( - isIconOnly: Boolean, - isRounded: Boolean, - buttonSize: PrezelButtonSize, - shapes: PrezelShapes = PrezelTheme.shapes, -): Shape = - when (isRounded) { - true -> shapes.V1000 - false -> { - when (buttonSize) { - PrezelButtonSize.REGULAR -> shapes.V8 - PrezelButtonSize.SMALL -> if (isIconOnly) shapes.V6 else shapes.V4 - PrezelButtonSize.XSMALL -> 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 || hierarchy == PrezelButtonHierarchy.SECONDARY) { - colors.bgLarge - } else { - colors.interactiveRegular - } - } - - 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 -> PrezelColorScheme.Dark.textLarge - PrezelButtonType.OUTLINED -> colors.interactiveRegular - PrezelButtonType.GHOST -> colors.interactiveRegular - } -} - -@Composable -private fun prezelButtonContentPadding( - size: PrezelButtonSize, - isIconOnly: Boolean, - spacing: PrezelSpacing = PrezelTheme.spacing, -): PaddingValues { - if (isIconOnly) return prezelIconButtonContentPadding(size) - - 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) -} - -@Composable -private fun prezelIconButtonContentPadding( - size: PrezelButtonSize, - spacing: PrezelSpacing = PrezelTheme.spacing, -): PaddingValues = - PaddingValues( - when (size) { - PrezelButtonSize.XSMALL -> spacing.V8 - PrezelButtonSize.SMALL -> spacing.V10 - PrezelButtonSize.REGULAR -> spacing.V14 - }, - ) - -@Composable -private fun prezelButtonIconSize(size: PrezelButtonSize): Dp = - when (size) { - PrezelButtonSize.XSMALL -> 14.dp - PrezelButtonSize.SMALL -> 16.dp - PrezelButtonSize.REGULAR -> 20.dp - } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelHyperlinkButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelHyperlinkButton.kt deleted file mode 100644 index b5b44380..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelHyperlinkButton.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.team.prezel.core.designsystem.component.button - -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.preview.ThemePreview -import com.team.prezel.core.designsystem.theme.PrezelTheme - -@Composable -fun PrezelHyperlinkButton( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - enabled: Boolean = true, - size: PrezelButtonSize = PrezelButtonSize.XSMALL, -) { - PrezelButton( - text = text, - onClick = onClick, - enabled = enabled, - modifier = modifier, - style = PrezelButtonStyle( - buttonType = PrezelButtonType.GHOST, - buttonHierarchy = PrezelButtonHierarchy.SECONDARY, - buttonSize = size, - showUnderline = true, - ), - ) -} - -@ThemePreview -@Composable -private fun PrezelHyperlinkButtonPreview() { - PrezelTheme { - PrezelHyperlinkButton( - text = "자세히 보기", - onClick = {}, - modifier = Modifier.padding(16.dp), - ) - } -} 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 deleted file mode 100644 index 8fc52ff4..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelIconButton.kt +++ /dev/null @@ -1,64 +0,0 @@ -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.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: IconSource, - 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, isIconOnly = true) - } -} - -@ThemePreview -@Composable -private fun PrezelIconButtonPreviewOutlined() { - PrezelTheme { - PrezelButtonPreviewByType(type = PrezelButtonType.OUTLINED, content = ::PrezelIconButtonPreviewItem, isIconOnly = true) - } -} - -@ThemePreview -@Composable -private fun PrezelIconButtonPreviewGhost() { - PrezelTheme { - PrezelButtonPreviewByType(type = PrezelButtonType.GHOST, content = ::PrezelIconButtonPreviewItem, isIconOnly = true) - } -} - -@Composable -private fun PrezelIconButtonPreviewItem( - style: PrezelButtonStyle, - enabled: Boolean, - modifier: Modifier = Modifier, -) { - PrezelIconButton( - icon = IconSource(resId = PrezelIcons.Blank), - onClick = {}, - enabled = enabled, - style = style, - modifier = modifier, - ) -} 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 deleted file mode 100644 index 0c60503d..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/PrezelTextButton.kt +++ /dev/null @@ -1,62 +0,0 @@ -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, - modifier: Modifier = Modifier, -) { - PrezelTextButton( - text = "Label", - onClick = {}, - enabled = enabled, - style = style, - modifier = modifier, - ) -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButton.kt deleted file mode 100644 index 44732ba2..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButton.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.team.prezel.core.designsystem.component.button.floating - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.FloatingActionButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -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 PrezelFloatingButton( - iconSource: IconSource, - onClick: () -> Unit, - modifier: Modifier = Modifier, - style: PrezelFloatingButtonStyle = PrezelFloatingButtonStyle(), -) { - FloatingActionButton( - onClick = onClick, - modifier = modifier - .applyPrezelFloatingButtonShadow() - .size(prezelFloatingButtonSize(style.size)), - shape = PrezelTheme.shapes.V1000, - containerColor = prezelFloatingButtonContainerColor(style.hierarchy), - contentColor = prezelFloatingButtonContentColor(style.hierarchy), - elevation = FloatingActionButtonDefaults.bottomAppBarFabElevation(), - ) { - Icon( - painter = iconSource.painter(), - contentDescription = iconSource.contentDescription(), - modifier = Modifier.size(prezelFloatingButtonIconSize(style.size)), - ) - } -} - -@ThemePreview -@Composable -private fun PrezelFloatingButtonPreview() { - PrezelTheme { - Column( - modifier = Modifier - .background(PrezelTheme.colors.bgRegular) - .padding(12.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - PrezelFloatingButton( - iconSource = IconSource(resId = PrezelIcons.Blank), - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.PRIMARY, size = PrezelFloatingButtonSize.REGULAR), - onClick = {}, - ) - - PrezelFloatingButton( - iconSource = IconSource(resId = PrezelIcons.Blank), - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.PRIMARY, size = PrezelFloatingButtonSize.SMALL), - onClick = {}, - ) - - PrezelFloatingButton( - iconSource = IconSource(resId = PrezelIcons.Blank), - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.SECONDARY, size = PrezelFloatingButtonSize.REGULAR), - onClick = {}, - ) - - PrezelFloatingButton( - iconSource = IconSource(resId = PrezelIcons.Blank), - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.SECONDARY, size = PrezelFloatingButtonSize.SMALL), - onClick = {}, - ) - } - } -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButtonMenuItem.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButtonMenuItem.kt deleted file mode 100644 index 49c07a4a..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButtonMenuItem.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.team.prezel.core.designsystem.component.button.floating - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.ripple -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp -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 PrezelFloatingButtonMenuItem( - label: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - iconSource: IconSource? = null, -) { - Row( - modifier = modifier - .fillMaxWidth() - .clip(PrezelTheme.shapes.V6) - .clickable( - indication = ripple(), - interactionSource = null, - onClick = onClick, - ).padding(prezelFloatingButtonMenuItemPaddingValues()), - verticalAlignment = Alignment.CenterVertically, - ) { - iconSource?.let { source -> PrezelFloatingButtonMenuItemIcon(iconSource = source) } - Text(text = label, style = prezelFloatingButtonMenuItemTextStyle()) - } -} - -@Composable -private fun PrezelFloatingButtonMenuItemIcon(iconSource: IconSource) { - Icon( - painter = iconSource.painter(), - contentDescription = iconSource.contentDescription(), - modifier = Modifier.size(prezelFloatingButtonMenuItemIconSize()), - ) - - Spacer(modifier = Modifier.width(prezelFloatingButtonMenuItemSpaceDp())) -} - -@ThemePreview -@Composable -private fun PrezelFloatingButtonMenuItemPreview() { - PrezelTheme { - Column( - verticalArrangement = Arrangement.spacedBy(8.dp), - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .background(PrezelTheme.colors.bgRegular) - .padding(16.dp), - ) { - PrezelFloatingButtonMenuItem( - label = "Label", - onClick = {}, - iconSource = IconSource(resId = PrezelIcons.Blank), - ) - - PrezelFloatingButtonMenuItem( - label = "Label", - onClick = {}, - ) - } - } -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButtonMenuItemStyle.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButtonMenuItemStyle.kt deleted file mode 100644 index 83716771..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButtonMenuItemStyle.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.team.prezel.core.designsystem.component.button.floating - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.runtime.Composable -import androidx.compose.runtime.compositionLocalOf -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.number.PrezelSpacing -import com.team.prezel.core.designsystem.foundation.typography.PrezelTypography -import com.team.prezel.core.designsystem.theme.PrezelTheme - -enum class PrezelFloatingButtonMenuItemSize { - SMALL, - REGULAR, - ; - - companion object { - fun buttonMenuItemSize(size: PrezelFloatingButtonSize): PrezelFloatingButtonMenuItemSize = - when (size) { - PrezelFloatingButtonSize.SMALL -> SMALL - PrezelFloatingButtonSize.REGULAR -> REGULAR - } - } -} - -internal val LocalPrezelFloatingButtonMenuItemSize = compositionLocalOf { PrezelFloatingButtonMenuItemSize.REGULAR } - -@Composable -internal fun prezelFloatingButtonMenuItemTextStyle( - size: PrezelFloatingButtonMenuItemSize = LocalPrezelFloatingButtonMenuItemSize.current, - typography: PrezelTypography = PrezelTheme.typography, -): TextStyle = - when (size) { - PrezelFloatingButtonMenuItemSize.SMALL -> typography.body3Regular - PrezelFloatingButtonMenuItemSize.REGULAR -> typography.body2Regular - } - -@Composable -internal fun prezelFloatingButtonMenuItemPaddingValues( - size: PrezelFloatingButtonMenuItemSize = LocalPrezelFloatingButtonMenuItemSize.current, - spacing: PrezelSpacing = PrezelTheme.spacing, -): PaddingValues = - when (size) { - PrezelFloatingButtonMenuItemSize.SMALL -> spacing.V8 to spacing.V4 - PrezelFloatingButtonMenuItemSize.REGULAR -> spacing.V12 to spacing.V8 - }.let { (horizontal, vertical) -> PaddingValues(horizontal = horizontal, vertical = vertical) } - -@Composable -internal fun prezelFloatingButtonMenuItemIconSize(size: PrezelFloatingButtonMenuItemSize = LocalPrezelFloatingButtonMenuItemSize.current): Dp = - when (size) { - PrezelFloatingButtonMenuItemSize.SMALL -> 16.dp - PrezelFloatingButtonMenuItemSize.REGULAR -> 20.dp - } - -@Composable -internal fun prezelFloatingButtonMenuItemSpaceDp( - size: PrezelFloatingButtonMenuItemSize = LocalPrezelFloatingButtonMenuItemSize.current, - spacing: PrezelSpacing = PrezelTheme.spacing, -): Dp = - when (size) { - PrezelFloatingButtonMenuItemSize.SMALL -> spacing.V4 - PrezelFloatingButtonMenuItemSize.REGULAR -> spacing.V8 - } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButtonStyle.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButtonStyle.kt deleted file mode 100644 index d9774d49..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingButtonStyle.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.team.prezel.core.designsystem.component.button.floating - -import androidx.compose.foundation.layout.PaddingValues -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.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.PrezelSpacing -import com.team.prezel.core.designsystem.theme.PrezelColorScheme -import com.team.prezel.core.designsystem.theme.PrezelTheme -import com.team.prezel.core.designsystem.util.dropShadowCache - -enum class PrezelFloatingButtonHierarchy { - PRIMARY, - SECONDARY, -} - -enum class PrezelFloatingButtonSize { - SMALL, - REGULAR, -} - -@Immutable -data class PrezelFloatingButtonStyle( - val hierarchy: PrezelFloatingButtonHierarchy = PrezelFloatingButtonHierarchy.PRIMARY, - val size: PrezelFloatingButtonSize = PrezelFloatingButtonSize.REGULAR, -) - -internal fun prezelFloatingButtonSize(size: PrezelFloatingButtonSize): Dp = - when (size) { - PrezelFloatingButtonSize.SMALL -> 36.dp - PrezelFloatingButtonSize.REGULAR -> 48.dp - } - -internal fun prezelFloatingButtonIconSize(size: PrezelFloatingButtonSize): Dp = - when (size) { - PrezelFloatingButtonSize.SMALL -> 16.dp - PrezelFloatingButtonSize.REGULAR -> 20.dp - } - -@Composable -internal fun Modifier.applyPrezelFloatingButtonShadow(): Modifier = - this.dropShadowCache( - color = Color(0x1F000713), - shape = PrezelTheme.shapes.V1000, - offsetY = 2.dp, - blurRadius = 4.dp, - ) - -@Composable -internal fun prezelFloatingButtonContentColor( - hierarchy: PrezelFloatingButtonHierarchy, - colors: PrezelColors = PrezelTheme.colors, -): Color = - when (hierarchy) { - PrezelFloatingButtonHierarchy.PRIMARY -> colors.solidWhite - PrezelFloatingButtonHierarchy.SECONDARY -> colors.iconRegular - } - -@Composable -internal fun prezelFloatingButtonContainerColor( - hierarchy: PrezelFloatingButtonHierarchy, - colors: PrezelColors = PrezelTheme.colors, -): Color = - when (hierarchy) { - PrezelFloatingButtonHierarchy.PRIMARY -> colors.interactiveRegular - PrezelFloatingButtonHierarchy.SECONDARY -> colors.bgLarge - } - -@Composable -internal fun prezelFloatingMenuButtonContentColor( - hierarchy: PrezelFloatingButtonHierarchy, - colors: PrezelColors = PrezelTheme.colors, -): Color = - when (hierarchy) { - PrezelFloatingButtonHierarchy.PRIMARY -> PrezelColorScheme.Light.textMedium - PrezelFloatingButtonHierarchy.SECONDARY -> colors.textMedium - } - -@Composable -internal fun prezelFloatingMenuButtonContainerColor( - hierarchy: PrezelFloatingButtonHierarchy, - colors: PrezelColors = PrezelTheme.colors, -): Color = - when (hierarchy) { - PrezelFloatingButtonHierarchy.PRIMARY -> colors.solidWhite - PrezelFloatingButtonHierarchy.SECONDARY -> colors.bgRegular - } - -@Composable -internal fun prezelFloatingMenuButtonPaddingValues( - size: PrezelFloatingButtonSize, - spacing: PrezelSpacing = PrezelTheme.spacing, -): PaddingValues = - when (size) { - PrezelFloatingButtonSize.SMALL -> spacing.V4 - PrezelFloatingButtonSize.REGULAR -> spacing.V6 - }.let { padding -> PaddingValues(padding) } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingMenuButton.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingMenuButton.kt deleted file mode 100644 index a94cff52..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/button/floating/PrezelFloatingMenuButton.kt +++ /dev/null @@ -1,234 +0,0 @@ -package com.team.prezel.core.designsystem.component.button.floating - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.IntrinsicSize -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredHeightIn -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.material3.LocalContentColor -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.R -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 PrezelFloatingMenuButton( - iconSource: IconSource, - isExpanded: Boolean, - onClick: () -> Unit, - modifier: Modifier = Modifier, - style: PrezelFloatingButtonStyle = PrezelFloatingButtonStyle(), - content: @Composable (ColumnScope.() -> Unit), -) { - CompositionLocalProvider( - LocalContentColor provides prezelFloatingMenuButtonContentColor(style.hierarchy), - ) { - Column( - modifier = modifier, - horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.spacedBy(PrezelTheme.spacing.V16), - ) { - PrezelFloatingButtonMenu( - isExpanded = isExpanded, - style = style, - content = content, - ) - - PrezelMainFloatingButton( - iconSource = iconSource, - style = style, - isExpanded = isExpanded, - onClick = onClick, - ) - } - } -} - -@Composable -private fun PrezelMainFloatingButton( - iconSource: IconSource, - style: PrezelFloatingButtonStyle, - isExpanded: Boolean, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { - val currentIconSource = - if (isExpanded) { - IconSource( - resId = PrezelIcons.Cancel, - contentDescResId = R.string.core_designsystem_close_floating_btn_content_desc, - ) - } else { - iconSource - } - - PrezelFloatingButton( - iconSource = currentIconSource, - onClick = onClick, - modifier = modifier, - style = style, - ) -} - -@Composable -private fun PrezelFloatingButtonMenu( - isExpanded: Boolean, - modifier: Modifier = Modifier, - style: PrezelFloatingButtonStyle, - content: @Composable ColumnScope.() -> Unit, -) { - AnimatedVisibility( - visible = isExpanded, - enter = fadeIn(), - exit = fadeOut(), - ) { - Column( - modifier = modifier - .width(IntrinsicSize.Max) - .background( - shape = PrezelTheme.shapes.V12, - color = prezelFloatingMenuButtonContainerColor(style.hierarchy), - ).padding(prezelFloatingMenuButtonPaddingValues(style.size)), - horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(PrezelTheme.spacing.V4), - ) { - CompositionLocalProvider( - LocalPrezelFloatingButtonMenuItemSize provides PrezelFloatingButtonMenuItemSize.buttonMenuItemSize(style.size), - ) { - content() - } - } - } -} - -@ThemePreview -@Composable -private fun PrimaryPrezelFloatingMenuButtonPreview() { - PrezelTheme { - Row( - modifier = Modifier - .wrapContentHeight() - .requiredHeightIn(200.dp) - .background(PrezelTheme.colors.bgScrim) - .padding(12.dp), - verticalAlignment = Alignment.Bottom, - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - PreviewFloatingMenuButton( - initialExpanded = false, - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.PRIMARY, size = PrezelFloatingButtonSize.REGULAR), - ) - PreviewFloatingMenuButton( - initialExpanded = true, - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.PRIMARY, size = PrezelFloatingButtonSize.REGULAR), - ) - PreviewFloatingMenuButton( - initialExpanded = false, - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.PRIMARY, size = PrezelFloatingButtonSize.SMALL), - ) - PreviewFloatingMenuButton( - initialExpanded = true, - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.PRIMARY, size = PrezelFloatingButtonSize.SMALL), - ) - } - } -} - -@ThemePreview -@Composable -private fun SecondaryPrezelFloatingMenuButtonPreview() { - PrezelTheme { - Row( - modifier = Modifier - .wrapContentHeight() - .requiredHeightIn(200.dp) - .background(PrezelTheme.colors.bgScrim) - .padding(12.dp), - verticalAlignment = Alignment.Bottom, - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - PreviewFloatingMenuButton( - initialExpanded = false, - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.SECONDARY, size = PrezelFloatingButtonSize.REGULAR), - ) - PreviewFloatingMenuButton( - initialExpanded = true, - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.SECONDARY, size = PrezelFloatingButtonSize.REGULAR), - ) - PreviewFloatingMenuButton( - initialExpanded = false, - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.SECONDARY, size = PrezelFloatingButtonSize.SMALL), - ) - PreviewFloatingMenuButton( - initialExpanded = true, - style = PrezelFloatingButtonStyle(hierarchy = PrezelFloatingButtonHierarchy.SECONDARY, size = PrezelFloatingButtonSize.SMALL), - ) - } - } -} - -@Composable -private fun PreviewFloatingMenuButton( - initialExpanded: Boolean, - style: PrezelFloatingButtonStyle, -) { - var isExpanded by remember { mutableStateOf(initialExpanded) } - - PrezelFloatingMenuButton( - iconSource = IconSource(resId = PrezelIcons.Blank), - isExpanded = isExpanded, - onClick = { isExpanded = !isExpanded }, - style = style, - ) { - PrezelFloatingButtonMenuItem(label = "LongLabel", onClick = {}) - PrezelFloatingButtonMenuItem(label = "Label", onClick = {}) - PrezelFloatingButtonMenuItem(label = "Label", onClick = {}) - } -} - -@ThemePreview -@Composable -private fun PrezelFloatingMenuButtonPreview() { - PrezelTheme { - Row { - PreviewFloatingMenuButton(true) - PreviewFloatingMenuButton(false) - } - } -} - -@Composable -private fun PreviewFloatingMenuButton(isShowIcon: Boolean) { - val iconSource = if (isShowIcon) IconSource(resId = PrezelIcons.Blank) else null - - PrezelFloatingMenuButton( - modifier = Modifier - .background(PrezelTheme.colors.bgScrim) - .padding(16.dp), - iconSource = IconSource(resId = PrezelIcons.Blank), - isExpanded = true, - onClick = {}, - ) { - PrezelFloatingButtonMenuItem(label = "LongLabel", onClick = {}, iconSource = iconSource) - PrezelFloatingButtonMenuItem(label = "Label", onClick = {}, iconSource = iconSource) - PrezelFloatingButtonMenuItem(label = "Label", onClick = {}, iconSource = iconSource) - } -} 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 96690379..a0478518 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,12 +1,12 @@ 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.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTextStyle @@ -16,21 +16,24 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.team.prezel.core.designsystem.icon.IconSource +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource 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.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 PrezelChip( modifier: Modifier = Modifier, text: String? = null, - icon: IconSource? = null, + @DrawableRes iconResId: Int? = null, style: PrezelChipStyle = PrezelChipStyle(), ) { val hasText = text != null - val hasIcon = icon != null + val hasIcon = iconResId != null val iconOnly = hasIcon && !hasText require(hasText || hasIcon) { "Chip은 text 또는 icon 중 하나는 반드시 필요합니다." } @@ -49,7 +52,9 @@ fun PrezelChip( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { - PrezelChipIcon(icon = icon, style = style) + iconResId?.let { resId -> + PrezelChipIcon(iconResId = resId, style = style) + } if (hasText) { if (hasIcon) { @@ -66,7 +71,7 @@ fun PrezelChip( fun PrezelChip( modifier: Modifier = Modifier, text: String? = null, - icon: IconSource? = null, + @DrawableRes iconResId: Int? = null, style: PrezelChipStyle = PrezelChipStyle(), customColors: PrezelChipColors = LocalPrezelChipColors.current, ) { @@ -76,7 +81,7 @@ fun PrezelChip( PrezelChip( modifier = modifier, text = text, - icon = icon, + iconResId = iconResId, style = style, ) } @@ -84,42 +89,141 @@ fun PrezelChip( @Composable private fun PrezelChipIcon( - icon: IconSource?, + @DrawableRes iconResId: Int, style: PrezelChipStyle, modifier: Modifier = Modifier, ) { - if (icon == null) return - Icon( - painter = icon.painter(), - contentDescription = icon.contentDescription(), + painter = painterResource(id = iconResId), + contentDescription = null, modifier = modifier.size(style.iconSize()), ) } -@ThemePreview +@BasicPreview @Composable -private fun PrezelChipPreview() { - PrezelTheme { - PreviewScaffold { - PrezelChipPreviewByType( - type = PrezelChipType.FILLED, - ) { style -> PrezelChipPreviewItem(style) } +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), + ) + } + } + } + } +} - HorizontalDivider() +@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), + ) + } + } + } + } +} - PrezelChipPreviewByType( - type = PrezelChipType.OUTLINED, - ) { style -> PrezelChipPreviewItem(style) } +@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), + ) + } + } } } } +@BasicPreview @Composable -private fun PrezelChipPreviewItem(style: PrezelChipStyle) { - PrezelChip( - text = "Label", - icon = IconSource(resId = PrezelIcons.Blank), - style = style, - ) +private fun PrezelChipCustomPreview() { + PreviewSection( + title = "Chip / Custom", + description = "사용자 정의된 색 지정이 가능합니다.", + ) { + PreviewRow { + PrezelChip( + text = "느려요", + 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, + ), + ) + + 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, + ), + ) + + 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), + ), + ) + } + } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChipPreview.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChipPreview.kt deleted file mode 100644 index 5c2a74d8..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelChipPreview.kt +++ /dev/null @@ -1,130 +0,0 @@ -package com.team.prezel.core.designsystem.component.chip - -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.ui.Modifier -import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.theme.PrezelTheme -import kotlinx.collections.immutable.persistentListOf - -internal typealias PrezelChipPreviewContent = @Composable (PrezelChipStyle) -> Unit - -private val DefaultInteractionVariants = persistentListOf( - PrezelChipInteraction.DEFAULT, - PrezelChipInteraction.ACTIVE, - PrezelChipInteraction.DISABLED, -) - -private val PreviewSizes = persistentListOf( - PrezelChipSize.SMALL, - PrezelChipSize.REGULAR, -) - -@Composable -internal fun PrezelChipPreviewByType( - type: PrezelChipType, - content: PrezelChipPreviewContent, -) { - Text( - text = type.name, - style = PrezelTheme.typography.title2Medium, - ) - - HorizontalDivider() - - PrezelChipBadSection( - type = type, - content = content, - ) - - HorizontalDivider() - - PrezelChipDefaultSection( - type = type, - content = content, - ) -} - -@Composable -private fun PrezelChipBadSection( - type: PrezelChipType, - content: PrezelChipPreviewContent, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier, - verticalArrangement = Arrangement.spacedBy(12.dp), - ) { - Text( - text = "Feedback: BAD", - style = PrezelTheme.typography.body2Medium, - ) - - PrezelChipPreviewBlock( - type = type, - feedback = PrezelChipFeedback.BAD, - interaction = PrezelChipInteraction.DEFAULT, - content = content, - ) - } -} - -@Composable -private fun PrezelChipDefaultSection( - type: PrezelChipType, - content: PrezelChipPreviewContent, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier, - verticalArrangement = Arrangement.spacedBy(12.dp), - ) { - Text( - text = "Feedback: DEFAULT", - style = PrezelTheme.typography.body2Medium, - ) - - DefaultInteractionVariants.forEach { interaction -> - Text( - text = "Interaction: $interaction", - style = PrezelTheme.typography.body3Medium, - ) - - PrezelChipPreviewBlock( - type = type, - feedback = PrezelChipFeedback.DEFAULT, - interaction = interaction, - content = content, - ) - } - } -} - -@Composable -private fun PrezelChipPreviewBlock( - type: PrezelChipType, - interaction: PrezelChipInteraction, - feedback: PrezelChipFeedback, - content: PrezelChipPreviewContent, - modifier: Modifier = Modifier, -) { - Row( - modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - PreviewSizes.forEach { size -> - content( - PrezelChipStyle( - type = type, - size = size, - interaction = interaction, - feedback = feedback, - ), - ) - } - } -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelCustomChipPreview.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelCustomChipPreview.kt deleted file mode 100644 index 1ab56489..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/chip/PrezelCustomChipPreview.kt +++ /dev/null @@ -1,176 +0,0 @@ -package com.team.prezel.core.designsystem.component.chip - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -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 - -@ThemePreview -@Composable -private fun PrezelChip_CustomColors_Preview() { - PrezelTheme { - PreviewScaffold { - CustomChipHeader() - CustomChipLabelSection() - CustomChipIconOnlySection() - } - } -} - -@Composable -private fun CustomChipHeader() { - Text( - text = "Custom Chip", - style = PrezelTheme.typography.title2Medium, - ) -} - -@Composable -private fun CustomChipLabelSection() { - FlowRow( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - maxItemsInEachRow = 3, - ) { - CustomYellowLabelChip() - CustomRedLabelChip() - CustomGreenLabelChip() - } -} - -@Composable -private fun CustomChipIconOnlySection() { - Text( - text = "Icon only", - style = PrezelTheme.typography.body3Medium, - ) - - Spacer(modifier = Modifier.height(8.dp)) - - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - CustomYellowIconChip() - CustomRedIconChip() - CustomGreenIconChip() - } -} - -@Composable -private fun CustomYellowLabelChip() { - PrezelChip( - text = "느려요", - icon = IconSource(resId = 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, - ), - ) -} - -@Composable -private fun CustomRedLabelChip() { - PrezelChip( - text = "빨라요", - icon = 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, - ), - ) -} - -@Composable -private fun CustomGreenLabelChip() { - PrezelChip( - text = "적당해요", - icon = null, - style = PrezelChipStyle( - type = PrezelChipType.FILLED, - size = PrezelChipSize.SMALL, - interaction = PrezelChipInteraction.ACTIVE, - feedback = PrezelChipFeedback.DEFAULT, - ), - customColors = PrezelChipColors( - containerColor = Color(0xFFDBFFF6), - contentColor = Color(0xFF00A37A), - ), - ) -} - -@Composable -private fun CustomYellowIconChip() { - PrezelChip( - text = null, - icon = IconSource(resId = 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, - ), - ) -} - -@Composable -private fun CustomRedIconChip() { - PrezelChip( - text = null, - icon = IconSource(resId = PrezelIcons.Blank), - style = PrezelChipStyle( - type = PrezelChipType.OUTLINED, - size = PrezelChipSize.REGULAR, - interaction = PrezelChipInteraction.DEFAULT, - feedback = PrezelChipFeedback.DEFAULT, - ), - customColors = PrezelChipColors( - containerColor = PrezelTheme.colors.feedbackBadSmall, - contentColor = PrezelTheme.colors.feedbackBadRegular, - ), - ) -} - -@Composable -private fun CustomGreenIconChip() { - PrezelChip( - text = null, - icon = IconSource(resId = PrezelIcons.Blank), - style = PrezelChipStyle( - type = PrezelChipType.FILLED, - size = PrezelChipSize.SMALL, - interaction = PrezelChipInteraction.ACTIVE, - feedback = PrezelChipFeedback.DEFAULT, - ), - customColors = PrezelChipColors( - containerColor = Color(0xFFDBFFF6), - contentColor = Color(0xFF00A37A), - ), - ) -} 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 4d78326a..3e7e6e17 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,49 +1,91 @@ package com.team.prezel.core.designsystem.component.chip -import androidx.compose.material3.HorizontalDivider +import androidx.annotation.DrawableRes import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -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 com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow @Composable fun PrezelIconChip( - icon: IconSource, + @DrawableRes iconResId: Int, modifier: Modifier = Modifier, style: PrezelChipStyle = PrezelChipStyle(), ) { PrezelChip( modifier = modifier, - icon = icon, + iconResId = iconResId, style = style, ) } -@ThemePreview +@BasicPreview @Composable -private fun PrezelIconChipPreview() { - PrezelTheme { - PreviewScaffold { - PrezelChipPreviewByType( - type = PrezelChipType.FILLED, - ) { style -> PrezelIconChipPreviewItem(style) } - - HorizontalDivider() +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), + ) + } + } + } + } +} - PrezelChipPreviewByType( - type = PrezelChipType.OUTLINED, - ) { style -> PrezelIconChipPreviewItem(style) } +@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), + ) + } + } } } } +@BasicPreview @Composable -private fun PrezelIconChipPreviewItem(style: PrezelChipStyle) { - PrezelIconChip( - icon = IconSource(resId = PrezelIcons.Blank), - style = style, - ) +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), + ) + } + } + } + } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DatePickerDayCellView.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DatePickerDayCellView.kt index 3aa2aaf7..86858124 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DatePickerDayCellView.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DatePickerDayCellView.kt @@ -3,11 +3,9 @@ package com.team.prezel.core.designsystem.component.datepicker import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text import androidx.compose.material3.ripple @@ -16,8 +14,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.preview.ThemePreview +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.theme.PrezelTheme import kotlinx.datetime.LocalDate @@ -61,11 +60,14 @@ internal fun RowScope.DayCellView( } } -@ThemePreview +@BasicPreview @Composable private fun DayCellViewPreview() { - PrezelTheme { - Row(modifier = Modifier.width(320.dp)) { + PreviewSection( + title = "DatePicker/Day", + description = "DatePicker에 사용되는 리소스입니다.", + ) { + PreviewRow { DayCellView( uiModel = DayCell( date = LocalDate(2024, 1, 1), diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthGrid.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthGrid.kt index 0d70efe2..74241f9a 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthGrid.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthGrid.kt @@ -7,7 +7,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.theme.PrezelTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.datetime.DayOfWeek @@ -68,10 +69,13 @@ private fun WeekRow( } } -@ThemePreview +@BasicPreview @Composable private fun MonthGridPreview() { - PrezelTheme { + PreviewSection( + title = "DatePicker/MonthGrid", + description = "DatePicker에 사용되는 리소스입니다.", + ) { MonthGrid( yearMonth = YearMonth(year = 2026, month = 2), selectedDate = LocalDate(year = 2026, month = 2, day = 26), diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthSection.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthSection.kt index 12a57bbd..8c553221 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthSection.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthSection.kt @@ -7,7 +7,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import com.team.prezel.core.designsystem.R -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.theme.PrezelTheme import kotlinx.datetime.LocalDate import kotlinx.datetime.YearMonth @@ -42,10 +43,13 @@ internal fun MonthSection( } } -@ThemePreview +@BasicPreview @Composable private fun MonthSectionPreview() { - PrezelTheme { + PreviewSection( + title = "Month Section", + description = "DatePicker에 사용되는 리소스입니다.", + ) { MonthSection( yearMonth = YearMonth(year = 2026, month = 2), selectedDate = LocalDate(year = 2026, month = 2, day = 26), diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/PrezelDatePicker.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/PrezelDatePicker.kt index 51730dce..2693736e 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/PrezelDatePicker.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/PrezelDatePicker.kt @@ -25,13 +25,15 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.R import com.team.prezel.core.designsystem.component.PrezelDividerType import com.team.prezel.core.designsystem.component.PrezelHorizontalDivider import com.team.prezel.core.designsystem.component.PrezelTopAppBar -import com.team.prezel.core.designsystem.component.button.ButtonAreaButtonSpec -import com.team.prezel.core.designsystem.component.button.PrezelButtonArea +import com.team.prezel.core.designsystem.component.actions.area.PrezelButtonArea import com.team.prezel.core.designsystem.icon.PrezelIcons +import com.team.prezel.core.designsystem.preview.PreviewDefaults +import com.team.prezel.core.designsystem.preview.PreviewSurface import com.team.prezel.core.designsystem.theme.PrezelTheme import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.LocalDate @@ -113,15 +115,15 @@ private fun DatePickerFooter( onConfirm: (LocalDate) -> Unit, ) { PrezelButtonArea( - mainButton = ButtonAreaButtonSpec( + isVertical = false, + showBackground = true, + ) { + MainButton( label = stringResource(R.string.core_designsystem_date_picker_confirm_btn), enabled = selectedDate != null, onClick = { selectedDate?.let(onConfirm) }, - ), - subButton = null, - isVertical = false, - showBackground = true, - ) + ) + } } @Composable @@ -154,11 +156,13 @@ private fun WeekdayRow() { @Preview(showBackground = true) @Composable private fun PrezelDatePickerPreview() { - PrezelTheme { - var selected by remember { - mutableStateOf(LocalDate(year = 2026, month = 2, day = 26)) - } + var selected by remember { + mutableStateOf(LocalDate(year = 2026, month = 2, day = 26)) + } + PreviewSurface( + defaults = PreviewDefaults(screenPadding = PaddingValues(0.dp)), + ) { PrezelDatePicker( title = "발표 날짜", today = LocalDate(year = 2026, month = 2, day = 23), diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/image/PrezelImage.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/image/PrezelImage.kt index 3c7c10f0..14176e03 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/image/PrezelImage.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/image/PrezelImage.kt @@ -3,16 +3,11 @@ package com.team.prezel.core.designsystem.component.image import androidx.annotation.DrawableRes import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -23,7 +18,9 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.component.PrezelAsyncImage import com.team.prezel.core.designsystem.icon.PrezelIcons -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +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 @@ -85,55 +82,51 @@ private fun Modifier.prezelImageContainer( .then(if (borderStroke != null) Modifier.border(borderStroke, shape) else Modifier) } -@ThemePreview +@BasicPreview @Composable private fun PrezelImagePreview() { - PrezelTheme { - Column( - modifier = Modifier - .background(PrezelTheme.colors.bgRegular) - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp), - ) { - PrezelImagePreviewItem( - label = "Drawable / No Round / No Border", + PreviewSection( + title = "Image", + description = "round/border 조합별 이미지 콘텐츠를 보여줍니다.", + ) { + PreviewValueRow(name = "No Round / No Border") { + PrezelImage( + resId = PrezelIcons.Blank, + contentDescription = "", + modifier = Modifier.size(100.dp), rounded = false, border = false, ) + } - PrezelImagePreviewItem( - label = "Drawable / Round / No Border", + PreviewValueRow(name = "Round / No Border") { + PrezelImage( + resId = PrezelIcons.Blank, + contentDescription = "", + modifier = Modifier.size(100.dp), rounded = true, border = false, ) + } - PrezelImagePreviewItem( - label = "Drawable / No Round / Border", + PreviewValueRow(name = "No Round / Border") { + PrezelImage( + resId = PrezelIcons.Blank, + contentDescription = "", + modifier = Modifier.size(100.dp), rounded = false, border = true, ) + } - PrezelImagePreviewItem( - label = "Drawable / Round / Border", + PreviewValueRow(name = "Round / Border") { + PrezelImage( + resId = PrezelIcons.Blank, + contentDescription = "", + modifier = Modifier.size(100.dp), rounded = true, border = true, ) } } } - -@Composable -private fun PrezelImagePreviewItem( - label: String, - rounded: Boolean, - border: Boolean, -) { - Text(text = label) - PrezelImage( - resId = PrezelIcons.Calendar, - contentDescription = label, - modifier = Modifier.size(100.dp), - rounded = rounded, - border = border, - ) -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/image/PrezelImageSource.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/image/PrezelImageSource.kt deleted file mode 100644 index 59700c76..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/image/PrezelImageSource.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.team.prezel.core.designsystem.component.image - -import androidx.compose.runtime.Immutable - -@Immutable -sealed interface PrezelImageSource { - @Immutable - data class Url( - val value: String, - ) : PrezelImageSource - - @Immutable - data class Drawable( - val resId: Int, - ) : PrezelImageSource -} 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 index cf2ffc5f..20b2e0d8 100644 --- 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 @@ -1,6 +1,5 @@ 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 @@ -15,13 +14,11 @@ 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.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.theme.PrezelTheme import com.team.prezel.core.designsystem.util.drawDashBorder @@ -74,24 +71,16 @@ private fun RowScope.PrezelListTitle( ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelListSmallPreview() { - PrezelTheme { - PreviewScaffold { - PrezelListPreviewBySize(PrezelListSize.SMALL) - } - } + PrezelListPreviewBySize(PrezelListSize.SMALL) } -@ThemePreview +@BasicPreview @Composable private fun PrezelListRegularPreview() { - PrezelTheme { - PreviewScaffold { - PrezelListPreviewBySize(PrezelListSize.REGULAR) - } - } + PrezelListPreviewBySize(PrezelListSize.REGULAR) } @Composable @@ -131,27 +120,21 @@ private fun PrezelListPreviewItem( @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) + PreviewSection( + title = "PrezelList - $size", + description = "점선 테두리는 컴포넌트 경계를 의미하며,\nnested 상태별 leading/trailing 조합을 확인할 수 있습니다.", + ) { + 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) + 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/snackbar/PrezelSnackbar.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/snackbar/PrezelSnackbar.kt index 5fbb2160..b3de7efd 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/snackbar/PrezelSnackbar.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/snackbar/PrezelSnackbar.kt @@ -1,6 +1,6 @@ package com.team.prezel.core.designsystem.component.snackbar -import androidx.compose.foundation.layout.Column +import androidx.annotation.DrawableRes import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -17,15 +17,15 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.component.button.PrezelButton -import com.team.prezel.core.designsystem.component.button.PrezelButtonSize -import com.team.prezel.core.designsystem.component.button.PrezelButtonStyle -import com.team.prezel.core.designsystem.component.button.PrezelButtonType +import com.team.prezel.core.designsystem.component.actions.button.PrezelButton +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonSize +import com.team.prezel.core.designsystem.component.actions.button.config.ButtonType import com.team.prezel.core.designsystem.foundation.typography.PrezelTextStyles -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.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.theme.PrezelColorScheme import com.team.prezel.core.designsystem.theme.PrezelTheme @@ -49,8 +49,8 @@ fun PrezelSnackbar( modifier = Modifier.padding(start = PrezelTheme.spacing.V16, end = PrezelTheme.spacing.V8), verticalAlignment = Alignment.CenterVertically, ) { - visuals.leadingIcon?.let { leadingIcon -> - PrezelSnackbarLeadingIcon(icon = leadingIcon) + visuals.leadingIconResId?.let { resId -> + PrezelSnackbarLeadingIcon(iconResId = resId) Spacer(Modifier.width(PrezelTheme.spacing.V8)) } @@ -65,8 +65,9 @@ fun PrezelSnackbar( Spacer(Modifier.width(PrezelTheme.spacing.V16)) PrezelButton( text = visuals.actionLabel, + type = ButtonType.GHOST, + size = ButtonSize.SMALL, onClick = { data.performAction() }, - style = PrezelButtonStyle(buttonType = PrezelButtonType.GHOST, buttonSize = PrezelButtonSize.SMALL), ) } } @@ -74,30 +75,31 @@ fun PrezelSnackbar( @Composable private fun PrezelSnackbarLeadingIcon( - icon: IconSource, + @DrawableRes iconResId: Int, modifier: Modifier = Modifier, ) { Icon( - painter = icon.painter(), - contentDescription = icon.contentDescription(), + painter = painterResource(id = iconResId), + contentDescription = null, modifier = modifier.size(20.dp), tint = PrezelColorScheme.Dark.iconLarge, ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelSnackBarPreview_Cases() { - PrezelTheme { - Column { - PrezelSnackbar( - data = previewData(message = "Message", actionLabel = "Action", leadingIcon = IconSource(PrezelIcons.Blank)), - ) + PreviewSection( + title = "Snackbar", + description = "유저에게 현재 단계에 대한 정보를 버튼과 함께 제공합니다.", + ) { + PrezelSnackbar( + data = previewData(message = "Message", actionLabel = "Action", iconResId = PrezelIcons.Blank), + ) - PrezelSnackbar( - data = previewData(message = "Message Message Message Message Message", actionLabel = "Action"), - ) - } + PrezelSnackbar( + data = previewData(message = "Message Message Message Message Message", actionLabel = "Action"), + ) } } @@ -105,7 +107,7 @@ private fun PrezelSnackBarPreview_Cases() { private fun previewData( message: String, actionLabel: String, - leadingIcon: IconSource? = null, + @DrawableRes iconResId: Int? = null, ): SnackbarData = PreviewSnackbarData( visuals = PrezelSnackbarVisuals( @@ -113,7 +115,7 @@ private fun previewData( actionLabel = actionLabel, withDismissAction = false, duration = SnackbarDuration.Short, - leadingIcon = leadingIcon, + leadingIconResId = iconResId, offsetY = 0.dp, ), ) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/snackbar/SnackbarHost.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/snackbar/SnackbarHost.kt index 19476013..9d2fe2ef 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/snackbar/SnackbarHost.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/snackbar/SnackbarHost.kt @@ -1,5 +1,6 @@ package com.team.prezel.core.designsystem.component.snackbar +import androidx.annotation.DrawableRes import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -9,7 +10,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.icon.IconSource internal data class PrezelSnackbarVisuals( override val message: String, @@ -17,7 +17,7 @@ internal data class PrezelSnackbarVisuals( override val withDismissAction: Boolean = false, override val duration: SnackbarDuration, val id: String? = null, - val leadingIcon: IconSource?, + @param:DrawableRes val leadingIconResId: Int?, val offsetY: Dp, ) : SnackbarVisuals @@ -25,7 +25,7 @@ suspend fun SnackbarHostState.showPrezelSnackbar( message: String, actionLabel: String, onAction: () -> Unit, - leadingIcon: IconSource? = null, + @DrawableRes leadingIconResId: Int? = null, duration: SnackbarDuration = SnackbarDuration.Short, id: String? = null, onDismiss: (() -> Unit)? = null, @@ -37,7 +37,7 @@ suspend fun SnackbarHostState.showPrezelSnackbar( actionLabel = actionLabel, duration = duration, id = id, - leadingIcon = leadingIcon, + leadingIconResId = leadingIconResId, offsetY = offsetY, ), ) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextArea.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextArea.kt index b8c84d08..afeadca3 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextArea.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextArea.kt @@ -24,12 +24,13 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.component.button.PrezelButton +import com.team.prezel.core.designsystem.component.actions.button.PrezelButton import com.team.prezel.core.designsystem.component.textfield.component.PrezelTextFieldLabel import com.team.prezel.core.designsystem.component.textfield.component.PrezelTextFieldPlaceholder import com.team.prezel.core.designsystem.component.textfield.component.PrezelTextFieldSupportingText -import com.team.prezel.core.designsystem.preview.PreviewScaffold -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewColumn +import com.team.prezel.core.designsystem.preview.PreviewSurface import com.team.prezel.core.designsystem.theme.PrezelTheme @Composable @@ -188,11 +189,14 @@ private fun PrezelTextAreaDecorationBox( } } -@ThemePreview +@BasicPreview @Composable private fun PrezelTextAreaPrezelPreview() { - PrezelTheme { - PreviewScaffold(verticalArrangement = Arrangement.spacedBy(4.dp)) { + PreviewSurface { + PreviewColumn( + scrollable = true, + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { PrezelTextAreaPreviewItem( label = "Interaction - Default / Feedback - Default", value = "", @@ -246,14 +250,14 @@ private fun PrezelTextAreaPrezelPreview() { } } -@ThemePreview +@BasicPreview @Composable private fun MainPrezelTextAreaPreview() { var value by remember { mutableStateOf("") } val focusManager = LocalFocusManager.current - PrezelTheme { - PreviewScaffold { + PreviewSurface { + PreviewColumn(scrollable = true) { PrezelTextArea( value = value, onValueChange = { newValue -> value = newValue }, diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextField.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextField.kt index e754dbc5..90608922 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextField.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextField.kt @@ -31,13 +31,14 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.component.button.PrezelButton +import com.team.prezel.core.designsystem.component.actions.button.PrezelButton import com.team.prezel.core.designsystem.component.textfield.component.PrezelTextFieldLabel import com.team.prezel.core.designsystem.component.textfield.component.PrezelTextFieldPlaceholder import com.team.prezel.core.designsystem.component.textfield.component.PrezelTextFieldSupportingText 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.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewColumn +import com.team.prezel.core.designsystem.preview.PreviewSurface import com.team.prezel.core.designsystem.theme.PrezelTheme @Composable @@ -179,11 +180,14 @@ private fun PrezelTextFieldDecorationBox( } } -@ThemePreview +@BasicPreview @Composable private fun PrezelTextFieldPreview() { - PrezelTheme { - PreviewScaffold(verticalArrangement = Arrangement.spacedBy(8.dp)) { + PreviewSurface { + PreviewColumn( + scrollable = true, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { PreviewTextFieldItem( label = "Interaction - Default / Feedback - Default", value = "", @@ -238,14 +242,14 @@ private fun PrezelTextFieldPreview() { } } -@ThemePreview +@BasicPreview @Composable private fun MainPrezelTextFieldPreview() { var value by remember { mutableStateOf("") } val focusManager = LocalFocusManager.current - PrezelTheme { - PreviewScaffold { + PreviewSurface { + PreviewColumn(scrollable = true) { PrezelTextField( value = value, onValueChange = { newValue -> value = newValue }, diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldLabel.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldLabel.kt index 2d4c46a9..a2baebe9 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldLabel.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldLabel.kt @@ -5,7 +5,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.theme.PrezelTheme @Composable @@ -22,10 +23,10 @@ internal fun PrezelTextFieldLabel( ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelTextFieldLabelPreview() { - PrezelTheme { + PreviewSection(title = "TextField Label") { PrezelTextFieldLabel(label = "Label", modifier = Modifier.padding(8.dp)) } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldPlaceholder.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldPlaceholder.kt index c32ac194..480ba2d6 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldPlaceholder.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldPlaceholder.kt @@ -6,7 +6,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.LineHeightStyle import androidx.compose.ui.unit.dp -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.theme.PrezelTheme @Composable @@ -28,10 +29,10 @@ internal fun PrezelTextFieldPlaceholder( ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelTextFieldPlaceholderPreview() { - PrezelTheme { + PreviewSection(title = "TextField Placeholder") { PrezelTextFieldPlaceholder( placeholder = "Placeholder", modifier = Modifier.padding(16.dp), diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldSupportingText.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldSupportingText.kt index e953fd62..81d7692d 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldSupportingText.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/component/PrezelTextFieldSupportingText.kt @@ -1,16 +1,13 @@ package com.team.prezel.core.designsystem.component.textfield.component -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.component.textfield.PrezelTextFieldFeedback import com.team.prezel.core.designsystem.component.textfield.PrezelTextFieldInteraction import com.team.prezel.core.designsystem.component.textfield.PrezelTextFieldState -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection import com.team.prezel.core.designsystem.theme.PrezelTheme @Composable @@ -27,34 +24,29 @@ internal fun PrezelTextFieldSupportingText( ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelTextFieldSupportingTextPreview() { - PrezelTheme { - Column( - modifier = Modifier.padding(8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - PrezelTextFieldSupportingText( - state = PrezelTextFieldState( - interaction = PrezelTextFieldInteraction.TYPED, - feedback = PrezelTextFieldFeedback.Default("헬퍼 메시지"), - ), - ) + PreviewSection(title = "Supporting Text") { + PrezelTextFieldSupportingText( + state = PrezelTextFieldState( + interaction = PrezelTextFieldInteraction.TYPED, + feedback = PrezelTextFieldFeedback.Default("헬퍼 메시지"), + ), + ) - PrezelTextFieldSupportingText( - state = PrezelTextFieldState( - interaction = PrezelTextFieldInteraction.TYPED, - feedback = PrezelTextFieldFeedback.Bad("헬퍼 메시지"), - ), - ) + PrezelTextFieldSupportingText( + state = PrezelTextFieldState( + interaction = PrezelTextFieldInteraction.TYPED, + feedback = PrezelTextFieldFeedback.Bad("헬퍼 메시지"), + ), + ) - PrezelTextFieldSupportingText( - state = PrezelTextFieldState( - interaction = PrezelTextFieldInteraction.TYPED, - feedback = PrezelTextFieldFeedback.Good("헬퍼 메시지"), - ), - ) - } + PrezelTextFieldSupportingText( + state = PrezelTextFieldState( + interaction = PrezelTextFieldInteraction.TYPED, + feedback = PrezelTextFieldFeedback.Good("헬퍼 메시지"), + ), + ) } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelRadius.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelRadius.kt index b042611d..04c0b12f 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelRadius.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelRadius.kt @@ -9,12 +9,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp -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.preview.TokenList +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow import com.team.prezel.core.designsystem.theme.PrezelTheme -import kotlinx.collections.immutable.persistentListOf object PrezelRadius { val V2 = 2.dp @@ -26,19 +24,14 @@ object PrezelRadius { val V1000 = 1000.dp } -@ThemePreview +@BasicPreview @Composable private fun RadiusTokensPreview() { - PrezelTheme { - PreviewScaffold { RadiusSection() } - } -} - -@Composable -private fun RadiusSection() { - SectionTitle(title = "Radius") - TokenList( - items = persistentListOf( + PreviewSection( + title = "Radius", + description = "둥근 정도에 사용하는 숫자입니다.", + ) { + listOf( "V2" to PrezelRadius.V2, "V4" to PrezelRadius.V4, "V6" to PrezelRadius.V6, @@ -46,20 +39,24 @@ private fun RadiusSection() { "V12" to PrezelRadius.V12, "V16" to PrezelRadius.V16, "V1000" to PrezelRadius.V1000, - ), - preview = { radius -> - val shape = RoundedCornerShape(radius) - Box( - modifier = Modifier - .size(40.dp) - .clip(shape) - .background(PrezelTheme.colors.bgLarge) - .border( - width = PrezelStroke.V1, - color = PrezelTheme.colors.borderRegular, - shape = shape, - ), - ) - }, - ) + ).forEach { (name, radius) -> + PreviewValueRow( + name = name, + valueLabel = "${radius.value}dp", + ) { + val shape = RoundedCornerShape(radius) + Box( + modifier = Modifier + .size(40.dp) + .clip(shape) + .background(PrezelTheme.colors.bgLarge) + .border( + width = PrezelStroke.V1, + color = PrezelTheme.colors.borderRegular, + shape = shape, + ), + ) + } + } + } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelShapes.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelShapes.kt index 15cad583..3d94cc7c 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelShapes.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelShapes.kt @@ -2,22 +2,17 @@ package com.team.prezel.core.designsystem.foundation.number import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp -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.preview.TokenRow +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow import com.team.prezel.core.designsystem.theme.PrezelTheme -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf object PrezelShapes { val V2 = RoundedCornerShape(PrezelRadius.V2) @@ -29,21 +24,13 @@ object PrezelShapes { val V1000 = RoundedCornerShape(PrezelRadius.V1000) } -@ThemePreview +@BasicPreview @Composable private fun ShapesTokensPreview() { - PrezelTheme { - PreviewScaffold { - ShapesSection() - } - } -} - -@Composable -private fun ShapesSection() { - SectionTitle(title = "Shapes") - ShapeList( - items = persistentListOf( + PreviewSection( + title = "Shapes", + ) { + listOf( "V2" to PrezelShapes.V2, "V4" to PrezelShapes.V4, "V6" to PrezelShapes.V6, @@ -51,27 +38,20 @@ private fun ShapesSection() { "V12" to PrezelShapes.V12, "V16" to PrezelShapes.V16, "V1000" to PrezelShapes.V1000, - ), - ) -} - -@Composable -private fun ShapeList(items: ImmutableList>) { - Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { - items.forEach { (name, shape) -> - TokenRow( - name = name, - valueLabel = "shape", - preview = { - Box( - modifier = Modifier - .size(40.dp) - .clip(shape) - .background(PrezelTheme.colors.bgLarge) - .border(PrezelStroke.V1, PrezelTheme.colors.borderRegular, shape), - ) - }, - ) + ).forEach { (name, shape) -> + PreviewValueRow(name = name) { + Box( + modifier = Modifier + .size(40.dp) + .clip(shape) + .background(PrezelTheme.colors.bgLarge) + .border( + width = PrezelStroke.V1, + color = PrezelTheme.colors.borderRegular, + shape = shape, + ), + ) + } } } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelSpacing.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelSpacing.kt index 2ec0074b..1b81c989 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelSpacing.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelSpacing.kt @@ -2,19 +2,16 @@ package com.team.prezel.core.designsystem.foundation.number import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment 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.preview.SectionTitle -import com.team.prezel.core.designsystem.preview.ThemePreview -import com.team.prezel.core.designsystem.preview.TokenList +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewColumn +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow import com.team.prezel.core.designsystem.theme.PrezelTheme -import kotlinx.collections.immutable.persistentListOf object PrezelSpacing { val V0 = 0.dp @@ -39,51 +36,48 @@ object PrezelSpacing { val V80 = 80.dp } -@ThemePreview +@BasicPreview @Composable private fun SpacingTokensPreview() { - PrezelTheme { - PreviewScaffold { - SpacingSection() + PreviewSection( + title = "Spacing", + description = "간격에 사용하는 숫자입니다.", + ) { + PreviewColumn(scrollable = true) { + listOf( + "V0" to PrezelSpacing.V0, + "V2" to PrezelSpacing.V2, + "V4" to PrezelSpacing.V4, + "V6" to PrezelSpacing.V6, + "V8" to PrezelSpacing.V8, + "V10" to PrezelSpacing.V10, + "V12" to PrezelSpacing.V12, + "V14" to PrezelSpacing.V14, + "V16" to PrezelSpacing.V16, + "V20" to PrezelSpacing.V20, + "V24" to PrezelSpacing.V24, + "V28" to PrezelSpacing.V28, + "V32" to PrezelSpacing.V32, + "V36" to PrezelSpacing.V36, + "V40" to PrezelSpacing.V40, + "V48" to PrezelSpacing.V48, + "V56" to PrezelSpacing.V56, + "V64" to PrezelSpacing.V64, + "V72" to PrezelSpacing.V72, + "V80" to PrezelSpacing.V80, + ).forEach { (name, spacing) -> + PreviewValueRow( + name = name, + valueLabel = "${spacing.value}dp", + ) { + Box( + modifier = Modifier + .height(8.dp) + .width(spacing) + .background(PrezelTheme.colors.interactiveRegular), + ) + } + } } } } - -@Composable -private fun SpacingSection() { - SectionTitle(title = "Spacing") - TokenList( - items = persistentListOf( - "V0" to PrezelSpacing.V0, - "V2" to PrezelSpacing.V2, - "V4" to PrezelSpacing.V4, - "V6" to PrezelSpacing.V6, - "V8" to PrezelSpacing.V8, - "V10" to PrezelSpacing.V10, - "V12" to PrezelSpacing.V12, - "V14" to PrezelSpacing.V14, - "V16" to PrezelSpacing.V16, - "V20" to PrezelSpacing.V20, - "V24" to PrezelSpacing.V24, - "V28" to PrezelSpacing.V28, - "V32" to PrezelSpacing.V32, - "V36" to PrezelSpacing.V36, - "V40" to PrezelSpacing.V40, - "V48" to PrezelSpacing.V48, - "V56" to PrezelSpacing.V56, - "V64" to PrezelSpacing.V64, - "V72" to PrezelSpacing.V72, - "V80" to PrezelSpacing.V80, - ), - preview = { spacing -> - Row(verticalAlignment = Alignment.CenterVertically) { - Box( - modifier = Modifier - .height(8.dp) - .width(spacing) - .background(PrezelTheme.colors.interactiveRegular), - ) - } - }, - ) -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelStroke.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelStroke.kt index d621f446..29d6cf1b 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelStroke.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/foundation/number/PrezelStroke.kt @@ -6,12 +6,10 @@ import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable 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.preview.SectionTitle -import com.team.prezel.core.designsystem.preview.ThemePreview -import com.team.prezel.core.designsystem.preview.TokenList +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow import com.team.prezel.core.designsystem.theme.PrezelTheme -import kotlinx.collections.immutable.persistentListOf object PrezelStroke { val V1 = 1.dp @@ -19,35 +17,31 @@ object PrezelStroke { val V4 = 4.dp } -@ThemePreview +@BasicPreview @Composable private fun StrokeTokensPreview() { - PrezelTheme { - PreviewScaffold { - StrokeSection() - } - } -} - -@Composable -private fun StrokeSection() { - SectionTitle(title = "Stroke") - TokenList( - items = persistentListOf( + PreviewSection( + title = "Stroke", + description = "테두리 굵기에 사용하는 숫자입니다.", + ) { + listOf( "V1" to PrezelStroke.V1, "V2" to PrezelStroke.V2, "V4" to PrezelStroke.V4, - ), - preview = { stroke -> - Box( - modifier = Modifier - .size(40.dp) - .border( - width = stroke, - color = PrezelTheme.colors.borderLarge, - shape = PrezelShapes.V8, - ), - ) - }, - ) + ).forEach { (name, stroke) -> + PreviewValueRow( + name = name, + valueLabel = "${stroke.value}dp", + ) { + Box( + modifier = Modifier + .size(40.dp) + .border( + width = stroke, + color = PrezelTheme.colors.borderLarge, + ), + ) + } + } + } } 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 deleted file mode 100644 index 74230d20..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/icon/IconSource.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.team.prezel.core.designsystem.icon - -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource - -@Stable -class IconSource private constructor( - private val painterProvider: @Composable () -> Painter, - private val contentDescProvider: @Composable () -> String?, -) { - @Composable - fun painter(): Painter = painterProvider() - - @Composable - fun contentDescription(): String? = contentDescProvider() - - constructor(painter: Painter, contentDescription: String? = null) : this( - painterProvider = { painter }, - contentDescProvider = { contentDescription }, - ) - - constructor( - @DrawableRes resId: Int, - @StringRes contentDescResId: Int? = null, - vararg args: String, - ) : this( - painterProvider = { painterResource(resId) }, - contentDescProvider = { contentDescResId?.let { stringResource(it, *args) } }, - ) -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/ThemePreview.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/BasicPreview.kt similarity index 56% rename from Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/ThemePreview.kt rename to Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/BasicPreview.kt index cf6a59e2..b1ca96e3 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/ThemePreview.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/BasicPreview.kt @@ -4,13 +4,8 @@ import android.content.res.Configuration import androidx.compose.ui.tooling.preview.Preview @Preview( - name = "LightTheme", showBackground = true, + backgroundColor = 0xFFFFFFFF, uiMode = Configuration.UI_MODE_NIGHT_NO, ) -// @Preview( -// name = "DarkTheme", -// showBackground = true, -// uiMode = Configuration.UI_MODE_NIGHT_YES, -// ) -annotation class ThemePreview +annotation class BasicPreview diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/PreviewComponent.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/PreviewComponent.kt index f91eaf85..9c5cb150 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/PreviewComponent.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/preview/PreviewComponent.kt @@ -3,103 +3,227 @@ package com.team.prezel.core.designsystem.preview import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.theme.PrezelTheme -import kotlinx.collections.immutable.ImmutableList +/** + * Preview 공통 레이아웃 설정입니다. + */ +@Immutable +internal data class PreviewDefaults( + val screenPadding: PaddingValues = PaddingValues(16.dp), + val sectionSpacing: Dp = 24.dp, + val itemSpacing: Dp = 12.dp, +) + +/** + * 디자인시스템 Preview의 기본 배경, 패딩, 테마를 제공합니다. + * + * 대부분의 Preview는 이 컴포넌트를 기본 루트로 사용합니다. + * 화면 전체 문맥이 필요한 경우에만 `PreviewScaffold`를 사용합니다. + */ @Composable -internal fun PreviewScaffold( - verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(16.dp), - content: @Composable () -> Unit, +internal fun PreviewSurface( + modifier: Modifier = Modifier, + defaults: PreviewDefaults = PreviewDefaults(), + contentAlignment: Alignment = Alignment.TopStart, + content: @Composable BoxScope.() -> Unit, ) { - Scaffold( - containerColor = PrezelTheme.colors.bgRegular, - contentColor = PrezelTheme.colors.textLarge, - ) { innerPadding -> - Column( - modifier = Modifier - .padding(innerPadding) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(16.dp), - verticalArrangement = verticalArrangement, + PrezelTheme { + Surface( + modifier = modifier.fillMaxWidth(), + color = PrezelTheme.colors.bgRegular, ) { - content() + Box( + modifier = Modifier + .fillMaxWidth() + .background(PrezelTheme.colors.bgRegular) + .padding(defaults.screenPadding), + contentAlignment = contentAlignment, + content = content, + ) } } } +/** + * Preview에서 여러 상태를 세로로 나열할 때 사용하는 공통 레이아웃입니다. + */ +@Composable +internal fun PreviewColumn( + modifier: Modifier = Modifier, + defaults: PreviewDefaults = PreviewDefaults(), + scrollable: Boolean = false, + verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(defaults.itemSpacing), + horizontalAlignment: Alignment.Horizontal = Alignment.Start, + content: @Composable ColumnScope.() -> Unit, +) { + PrezelTheme { + Column( + modifier = modifier + .fillMaxWidth() + .then( + if (scrollable) { + Modifier.verticalScroll(rememberScrollState()) + } else { + Modifier + }, + ), + verticalArrangement = verticalArrangement, + horizontalAlignment = horizontalAlignment, + content = content, + ) + } +} + +/** + * Preview에서 작은 컴포넌트를 가로로 비교할 때 사용하는 공통 레이아웃입니다. + */ @Composable -internal fun SectionTitle(title: String) { - Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - Text(text = title, style = PrezelTheme.typography.title2Bold, color = PrezelTheme.colors.textLarge) - HorizontalDivider(color = PrezelTheme.colors.borderRegular) +internal fun PreviewRow( + modifier: Modifier = Modifier, + defaults: PreviewDefaults = PreviewDefaults(), + verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, + content: @Composable RowScope.() -> Unit, +) { + PrezelTheme { + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(defaults.itemSpacing), + verticalAlignment = verticalAlignment, + content = content, + ) } } +/** + * Preview 내에서 상태 그룹을 제목과 함께 구분해 보여줍니다. + */ @Composable -internal fun TokenList( - items: ImmutableList>, - preview: @Composable (Dp) -> Unit, +internal fun PreviewSection( + title: String, + modifier: Modifier = Modifier, + defaults: PreviewDefaults = PreviewDefaults(), + description: String? = null, + content: @Composable ColumnScope.() -> Unit, ) { - Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { - items.forEach { (name, value) -> - TokenRow( - name = name, - valueLabel = "${value.value}dp", - preview = { preview(value) }, + PreviewSurface( + modifier = modifier.fillMaxWidth(), + defaults = defaults, + ) { + Column(verticalArrangement = Arrangement.spacedBy(defaults.itemSpacing)) { + Text( + text = title, + style = PrezelTheme.typography.title2Medium, + color = PrezelTheme.colors.textLarge, + ) + + description?.let { value -> + Text( + text = value, + style = PrezelTheme.typography.body3Regular, + color = PrezelTheme.colors.textRegular, + ) + } + + HorizontalDivider(color = PrezelTheme.colors.borderRegular) + + PreviewColumn( + defaults = defaults, + content = content, ) } } } +/** + * Preview에서 토큰처럼 이름, 값, 샘플 UI를 한 줄에 보여줄 때 사용하는 행 레이아웃입니다. + */ @Composable -internal fun TokenRow( +internal fun PreviewValueRow( name: String, - valueLabel: String, + modifier: Modifier = Modifier, + defaults: PreviewDefaults = PreviewDefaults(), + valueLabel: String? = null, preview: @Composable () -> Unit, ) { Row( - modifier = Modifier.fillMaxWidth(), + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(defaults.itemSpacing), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp), ) { - Text( - text = name, - style = PrezelTheme.typography.body3Medium, - color = PrezelTheme.colors.textMedium, - modifier = Modifier.width(120.dp), - ) - Text( - text = valueLabel, - style = PrezelTheme.typography.body3Regular, - color = PrezelTheme.colors.textSmall, - modifier = Modifier.weight(1f), - ) - Spacer(modifier = Modifier.weight(1f)) - Box( - modifier = Modifier - .background(Color.Transparent) - .padding(4.dp), - ) { + Row(modifier = Modifier.weight(1f)) { + Text( + text = name, + style = PrezelTheme.typography.body3Medium, + color = PrezelTheme.colors.textMedium, + modifier = Modifier.weight(1f), + ) + valueLabel?.let { label -> + Text( + text = label, + style = PrezelTheme.typography.body3Regular, + color = PrezelTheme.colors.textSmall, + modifier = Modifier.weight(1f), + ) + } + } + Box(modifier = Modifier, contentAlignment = Alignment.CenterEnd) { preview() } } } + +/** + * TopBar, BottomBar, Snackbar 등 Scaffold 문맥이 필요한 Preview를 위한 래퍼입니다. + */ +@Composable +internal fun PreviewScaffold( + modifier: Modifier = Modifier, + defaults: PreviewDefaults = PreviewDefaults(), + topBar: @Composable () -> Unit = {}, + bottomBar: @Composable () -> Unit = {}, + floatingActionButton: @Composable () -> Unit = {}, + snackbarHost: @Composable () -> Unit = {}, + content: @Composable (PaddingValues) -> Unit, +) { + PrezelTheme { + Scaffold( + modifier = modifier.fillMaxSize(), + topBar = topBar, + bottomBar = bottomBar, + floatingActionButton = floatingActionButton, + snackbarHost = snackbarHost, + containerColor = PrezelTheme.colors.bgRegular, + ) { innerPadding -> + Box( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + .padding(defaults.screenPadding), + ) { + content(innerPadding) + } + } + } +} 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 bffc31b2..49120df1 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 @@ -2,12 +2,9 @@ package com.team.prezel.core.designsystem.theme import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -16,10 +13,9 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.foundation.color.ColorTokens import com.team.prezel.core.designsystem.foundation.color.PrezelColors -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.preview.TokenRow +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import java.util.Locale @@ -104,143 +100,127 @@ internal object PrezelColorScheme { ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelColorSchemeInteractivePreview() { - PrezelTheme { - PrezelColorsPreviewSection( - title = "Interactive", - description = "브랜드 색으로 활성화 상태 및 강조가 필요한 요소에 사용하는 색상입니다.", - items = persistentListOf( - "XSmall" to PrezelTheme.colors.interactiveXSmall, - "Small" to PrezelTheme.colors.interactiveSmall, - "Regular" to PrezelTheme.colors.interactiveRegular, - ), - ) - } + PrezelColorsPreviewSection( + title = "Interactive", + description = "브랜드 색으로 활성화 상태 및 강조가 필요한 요소에 사용하는 색상입니다.", + items = persistentListOf( + "XSmall" to PrezelColorScheme.Light.interactiveXSmall, + "Small" to PrezelColorScheme.Light.interactiveSmall, + "Regular" to PrezelColorScheme.Light.interactiveRegular, + ), + ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelColorSchemeBackgroundPreview() { - PrezelTheme { - PrezelColorsPreviewSection( - title = "Background", - description = "콘텐츠의 구조와 계층을 구분하는 색상입니다.", - items = persistentListOf( - "Regular" to PrezelTheme.colors.bgRegular, - "Medium" to PrezelTheme.colors.bgMedium, - "Large" to PrezelTheme.colors.bgLarge, - "Scrim" to PrezelTheme.colors.bgScrim, - "Disabled" to PrezelTheme.colors.bgDisabled, - ), - ) - } + PrezelColorsPreviewSection( + title = "Background", + description = "콘텐츠의 구조와 계층을 구분하는 색상입니다.", + items = persistentListOf( + "Regular" to PrezelColorScheme.Light.bgRegular, + "Medium" to PrezelColorScheme.Light.bgMedium, + "Large" to PrezelColorScheme.Light.bgLarge, + "Scrim" to PrezelColorScheme.Light.bgScrim, + "Disabled" to PrezelColorScheme.Light.bgDisabled, + ), + ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelColorSchemeTextPreview() { - PrezelTheme { - PrezelColorsPreviewSection( - title = "Text", - description = "정보와 콘텐츠를 명확하게 전달하기 위해 사용하는 색상입니다.", - items = persistentListOf( - "Small" to PrezelTheme.colors.textSmall, - "Regular" to PrezelTheme.colors.textRegular, - "Medium" to PrezelTheme.colors.textMedium, - "Large" to PrezelTheme.colors.textLarge, - "Disabled" to PrezelTheme.colors.textDisabled, - ), - ) - } + PrezelColorsPreviewSection( + title = "Text", + description = "정보와 콘텐츠를 명확하게 전달하기 위해 사용하는 색상입니다.", + items = persistentListOf( + "Small" to PrezelColorScheme.Light.textSmall, + "Regular" to PrezelColorScheme.Light.textRegular, + "Medium" to PrezelColorScheme.Light.textMedium, + "Large" to PrezelColorScheme.Light.textLarge, + "Disabled" to PrezelColorScheme.Light.textDisabled, + ), + ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelColorSchemeIconPreview() { - PrezelTheme { - PrezelColorsPreviewSection( - title = "Icon", - description = "기능적 액션과 시각적 커뮤니케이션을 돕기 위해 사용하는 색상입니다.", - items = persistentListOf( - "Regular" to PrezelTheme.colors.iconRegular, - "Medium" to PrezelTheme.colors.iconMedium, - "Disabled" to PrezelTheme.colors.iconDisabled, - ), - ) - } + PrezelColorsPreviewSection( + title = "Icon", + description = "기능적 액션과 시각적 커뮤니케이션을 돕기 위해 사용하는 색상입니다.", + items = persistentListOf( + "Regular" to PrezelColorScheme.Light.iconRegular, + "Medium" to PrezelColorScheme.Light.iconMedium, + "Disabled" to PrezelColorScheme.Light.iconDisabled, + ), + ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelColorSchemeBorderPreview() { - PrezelTheme { - PrezelColorsPreviewSection( - title = "Border", - description = "콘텐츠의 영역과 구조를 구분하는 색상입니다.", - items = persistentListOf( - "Small" to PrezelTheme.colors.borderSmall, - "Regular" to PrezelTheme.colors.borderRegular, - "Medium" to PrezelTheme.colors.borderMedium, - "Large" to PrezelTheme.colors.borderLarge, - "Disabled" to PrezelTheme.colors.borderDisabled, - ), - ) - } + PrezelColorsPreviewSection( + title = "Border", + description = "콘텐츠의 영역과 구조를 구분하는 색상입니다.", + items = persistentListOf( + "Small" to PrezelColorScheme.Light.borderSmall, + "Regular" to PrezelColorScheme.Light.borderRegular, + "Medium" to PrezelColorScheme.Light.borderMedium, + "Large" to PrezelColorScheme.Light.borderLarge, + "Disabled" to PrezelColorScheme.Light.borderDisabled, + ), + ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelColorSchemeFeedbackPreview() { - PrezelTheme { - PrezelColorsPreviewSection( - title = "Feedback", - description = "성공, 오류, 경고 상황을 명확하게 전달하기 위해 의미별로 정의된 색상입니다.", - items = persistentListOf( - "GoodSmall" to PrezelTheme.colors.feedbackGoodSmall, - "GoodRegular" to PrezelTheme.colors.feedbackGoodRegular, - "BadSmall" to PrezelTheme.colors.feedbackBadSmall, - "BadRegular" to PrezelTheme.colors.feedbackBadRegular, - "WarningSmall" to PrezelTheme.colors.feedbackWarningSmall, - "WarningRegular" to PrezelTheme.colors.feedbackWarningRegular, - ), - ) - } + PrezelColorsPreviewSection( + title = "Feedback", + description = "성공, 오류, 경고 상황을 명확하게 전달하기 위해 의미별로 정의된 색상입니다.", + items = persistentListOf( + "GoodSmall" to PrezelColorScheme.Light.feedbackGoodSmall, + "GoodRegular" to PrezelColorScheme.Light.feedbackGoodRegular, + "BadSmall" to PrezelColorScheme.Light.feedbackBadSmall, + "BadRegular" to PrezelColorScheme.Light.feedbackBadRegular, + "WarningSmall" to PrezelColorScheme.Light.feedbackWarningSmall, + "WarningRegular" to PrezelColorScheme.Light.feedbackWarningRegular, + ), + ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelColorSchemeAccentPreview() { - PrezelTheme { - PrezelColorsPreviewSection( - title = "Accent", - description = "콘텐츠의 주목성과 인지도를 높이기 위해 버튼, 액션, 강조 요소 등에 사용되는 포인트 색상입니다.", - items = persistentListOf( - "PurpleSmall" to PrezelTheme.colors.accentPurpleSmall, - "PurpleRegular" to PrezelTheme.colors.accentPurpleRegular, - "TealSmall" to PrezelTheme.colors.accentTealSmall, - "TealRegular" to PrezelTheme.colors.accentTealRegular, - "MagentaSmall" to PrezelTheme.colors.accentMagentaSmall, - "MagentaRegular" to PrezelTheme.colors.accentMagentaRegular, - ), - ) - } + PrezelColorsPreviewSection( + title = "Accent", + description = "콘텐츠의 주목성과 인지도를 높이기 위해 버튼, 액션, 강조 요소 등에 사용되는 포인트 색상입니다.", + items = persistentListOf( + "PurpleSmall" to PrezelColorScheme.Light.accentPurpleSmall, + "PurpleRegular" to PrezelColorScheme.Light.accentPurpleRegular, + "TealSmall" to PrezelColorScheme.Light.accentTealSmall, + "TealRegular" to PrezelColorScheme.Light.accentTealRegular, + "MagentaSmall" to PrezelColorScheme.Light.accentMagentaSmall, + "MagentaRegular" to PrezelColorScheme.Light.accentMagentaRegular, + ), + ) } -@ThemePreview +@BasicPreview @Composable private fun PrezelColorSchemeSolidPreview() { - PrezelTheme { - PrezelColorsPreviewSection( - title = "Solid", - description = "흰색과 검정색을 제공하여 시각적 대비, 보조, 구분 등에 활용되는 절대값 색상입니다.", - items = persistentListOf( - "White" to PrezelTheme.colors.solidWhite, - "Black" to PrezelTheme.colors.solidBlack, - ), - ) - } + PrezelColorsPreviewSection( + title = "Solid", + description = "흰색과 검정색을 제공하여 시각적 대비, 보조, 구분 등에 활용되는 절대값 색상입니다.", + items = persistentListOf( + "White" to PrezelColorScheme.Light.solidWhite, + "Black" to PrezelColorScheme.Light.solidBlack, + ), + ) } @Composable @@ -248,26 +228,22 @@ private fun PrezelColorsPreviewSection( title: String, description: String, items: ImmutableList>, - colors: PrezelColors = PrezelTheme.colors, ) { - PreviewScaffold { - Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { - SectionTitle(title = title) - Text(text = description, color = colors.textLarge.copy(alpha = 0.7f)) - - items.forEach { (name, color) -> - TokenRow( - name = name, - valueLabel = String.format(Locale.ROOT, "#%08X", color.toArgb()), - preview = { - Box( - modifier = Modifier - .size(32.dp) - .clip(RoundedCornerShape(8.dp)) - .background(color) - .border(0.3.dp, PrezelTheme.colors.solidBlack, RoundedCornerShape(8.dp)), - ) - }, + PreviewSection( + title = title, + description = description, + ) { + items.forEach { (name, color) -> + PreviewValueRow( + name = name, + valueLabel = String.format(Locale.ROOT, "#%08X", color.toArgb()), + ) { + Box( + modifier = Modifier + .size(36.dp) + .clip(RoundedCornerShape(8.dp)) + .background(color) + .border(0.3.dp, PrezelTheme.colors.solidBlack, RoundedCornerShape(8.dp)), ) } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelTypographyScheme.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelTypographyScheme.kt index 6d4b03d6..bc5af8b6 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelTypographyScheme.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/theme/PrezelTypographyScheme.kt @@ -1,9 +1,7 @@ package com.team.prezel.core.designsystem.theme import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text @@ -12,11 +10,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.isSpecified import com.team.prezel.core.designsystem.foundation.typography.PrezelTextStyles import com.team.prezel.core.designsystem.foundation.typography.PrezelTypography -import com.team.prezel.core.designsystem.preview.PreviewScaffold -import com.team.prezel.core.designsystem.preview.SectionTitle +import com.team.prezel.core.designsystem.preview.PreviewSection +import com.team.prezel.core.designsystem.preview.PreviewValueRow import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -46,122 +43,78 @@ internal object PrezelTypographyScheme { @Preview( showBackground = true, - device = "spec:width=800dp,height=600dp", - fontScale = 10f, + device = "spec:width=900dp,height=600dp", ) +private annotation class TypographyPreview + +@TypographyPreview @Composable private fun PrezelTypographyTitlePreview() { - PrezelTheme { - PrezelTypographyPreviewContent( - title = "Title", - items = persistentListOf( - "Title1 Medium" to PrezelTheme.typography.title1Medium, - "Title1 Bold" to PrezelTheme.typography.title1Bold, - "Title2 Medium" to PrezelTheme.typography.title2Medium, - "Title2 Bold" to PrezelTheme.typography.title2Bold, - ), - ) - } + val typography = PrezelTypographyScheme.Default() + PrezelTypographyPreviewContent( + title = "Title", + description = "페이지나 섹션의 주요 제목 등 핵심 정보를 강조하는 데 사용하는 스타일입니다.", + items = persistentListOf( + "Title1 Medium" to typography.title1Medium, + "Title1 Bold" to typography.title1Bold, + "Title2 Medium" to typography.title2Medium, + "Title2 Bold" to typography.title2Bold, + ), + ) } -@Preview( - showBackground = true, - device = "spec:width=800dp,height=1000dp", - fontScale = 10f, -) +@TypographyPreview @Composable private fun PrezelTypographyBodyPreview() { - PrezelTheme { - PrezelTypographyPreviewContent( - title = "Body", - items = persistentListOf( - "Body1 Regular" to PrezelTheme.typography.body1Regular, - "Body1 Medium" to PrezelTheme.typography.body1Medium, - "Body1 Bold" to PrezelTheme.typography.body1Bold, - "Body2 Regular" to PrezelTheme.typography.body2Regular, - "Body2 Medium" to PrezelTheme.typography.body2Medium, - "Body2 Bold" to PrezelTheme.typography.body2Bold, - "Body3 Regular" to PrezelTheme.typography.body3Regular, - "Body3 Medium" to PrezelTheme.typography.body3Medium, - "Body3 Bold" to PrezelTheme.typography.body3Bold, - ), - ) - } + val typography = PrezelTypographyScheme.Default() + PrezelTypographyPreviewContent( + title = "Body", + description = "일반 본문과 설명, 단일 문장 또는 단락 등의 주된 콘텐츠 전달에 사용하는 스타일입니다.", + items = persistentListOf( + "Body1 Regular" to typography.body1Regular, + "Body1 Medium" to typography.body1Medium, + "Body1 Bold" to typography.body1Bold, + "Body2 Regular" to typography.body2Regular, + "Body2 Medium" to typography.body2Medium, + "Body2 Bold" to typography.body2Bold, + "Body3 Regular" to typography.body3Regular, + "Body3 Medium" to typography.body3Medium, + "Body3 Bold" to typography.body3Bold, + ), + ) } -@Preview( - showBackground = true, - device = "spec:width=800dp,height=600dp", - fontScale = 10f, -) +@TypographyPreview @Composable private fun PrezelTypographyCaptionPreview() { - PrezelTheme { - PrezelTypographyPreviewContent( - title = "Caption", - items = persistentListOf( - "Caption1 Regular" to PrezelTheme.typography.caption1Regular, - "Caption1 Medium" to PrezelTheme.typography.caption1Medium, - "Caption2 Regular" to PrezelTheme.typography.caption2Regular, - "Caption2 Medium" to PrezelTheme.typography.caption2Medium, - ), - ) - } + val typography = PrezelTypographyScheme.Default() + PrezelTypographyPreviewContent( + title = "Caption", + description = "부가 정보, 안내, 설명, 작성 시간 등 메인 콘텐츠를 보조하는 짧은 정보를 전달할 때 사용하는 스타일입니다.", + items = persistentListOf( + "Caption1 Regular" to typography.caption1Regular, + "Caption1 Medium" to typography.caption1Medium, + "Caption2 Regular" to typography.caption2Regular, + "Caption2 Medium" to typography.caption2Medium, + ), + ) } @Composable private fun PrezelTypographyPreviewContent( title: String, + description: String, items: ImmutableList>, ) { - PreviewScaffold { - Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { - SectionTitle(title = title) - - Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { - items.forEach { (name, style) -> - TypographyRow(name = name, style = style) - } - } - } - } -} - -@Composable -private fun TypographyRow( - name: String, - style: TextStyle, -) { - Column( - modifier = Modifier - .fillMaxSize() - .background( - color = PrezelTheme.colors.bgRegular, - shape = RoundedCornerShape(12.dp), - ), - verticalArrangement = Arrangement.spacedBy(6.dp), + PreviewSection( + title = title, + description = description, ) { - Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { - val fontSize = style.fontSize - val lineHeight = style.lineHeight - val letterSpacing = style.letterSpacing - val fontWeight = style.fontWeight - - Text( - text = buildString { - append("$name (") - append("size: $fontSize") - if (lineHeight.isSpecified) append(" · lh: $lineHeight") - if (letterSpacing.isSpecified) append(" · ls: $letterSpacing") - if (fontWeight != null) append(" · w: ${fontWeight.weight}") - append(")") - }, - style = PrezelTheme.typography.body3Bold, - color = PrezelTheme.colors.textMedium, - ) + items.forEach { (name, style) -> + PreviewValueRow(name = name) { + TypographySampleText(style = style) + } } - - TypographySampleText(style = style) } } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DrawDashBorder.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DrawDashBorder.kt index 2c794cb4..d70115b1 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DrawDashBorder.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DrawDashBorder.kt @@ -15,8 +15,8 @@ import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape 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 +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.preview.PreviewSection /** * 점선 테두리를 그리는 [Modifier] 확장 함수. @@ -70,10 +70,13 @@ fun Modifier.drawDashBorder( } } -@ThemePreview +@BasicPreview @Composable private fun DrawDashBorderPreview() { - PrezelTheme { + PreviewSection( + title = "Dashed Border", + description = "테두리 외곽선을 점선으로 표시합니다.", + ) { Box( modifier = Modifier .fillMaxWidth() diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DropShadowCache.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DropShadowCache.kt deleted file mode 100644 index dd5a61f3..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/util/DropShadowCache.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.team.prezel.core.designsystem.util - -import android.graphics.BlurMaskFilter -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithCache -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Paint -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.addOutline -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -fun Modifier.dropShadowCache( - color: Color = Color.Black, - offsetX: Dp = 0.dp, - offsetY: Dp = 0.dp, - blurRadius: Dp = 0.dp, - shape: Shape, - blurStyle: BlurMaskFilter.Blur = BlurMaskFilter.Blur.NORMAL, -): Modifier = - drawWithCache { - val paint = Paint() - val frameworkPaint = paint.asFrameworkPaint() - if (blurRadius != 0.dp) { - frameworkPaint.maskFilter = (BlurMaskFilter(blurRadius.toPx(), blurStyle)) - } - frameworkPaint.color = color.toArgb() - - val leftPixel = offsetX.toPx() - val topPixel = offsetY.toPx() - - val path = Path().apply { - addOutline(shape.createOutline(size, layoutDirection, this@drawWithCache)) - } - - onDrawBehind { - drawIntoCanvas { canvas -> - canvas.translate(leftPixel, topPixel) - canvas.drawPath(path, paint) - canvas.translate(-leftPixel, -topPixel) - } - } - } diff --git a/Prezel/detekt-config.yml b/Prezel/detekt-config.yml index b7597b2f..d0af157f 100644 --- a/Prezel/detekt-config.yml +++ b/Prezel/detekt-config.yml @@ -3,61 +3,53 @@ build: config: validation: true - warningsAsErrors: false - checkExhaustiveness: false - -processors: - active: true style: - active: true + ReturnCount: + 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 + - BasicPreview + - TypographyPreview complexity: - active: true LongMethod: - active: true threshold: 50 ignoreAnnotated: - Preview - - ThemePreview - LongParameterList: - active: false - CyclomaticComplexMethod: - active: true - threshold: 15 + - BasicPreview NestedBlockDepth: - active: true threshold: 4 + LongParameterList: + active: true + functionThreshold: 10 + constructorThreshold: 10 + ignoreAnnotated: + - Composable + TooManyFunctions: + active: true + thresholdInFiles: 20 + thresholdInClasses: 15 + thresholdInInterfaces: 12 + thresholdInObjects: 20 + thresholdInEnums: 10 + ignoreAnnotatedFunctions: + - Preview naming: - active: true - MatchingDeclarationName: - active: false FunctionNaming: active: true ignoreAnnotated: - Composable - Preview - -potential-bugs: - active: true + MatchingDeclarationName: + active: false exceptions: active: true diff --git a/Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt b/Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt index 0050393f..0cdda672 100644 --- a/Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt +++ b/Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt @@ -24,8 +24,8 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.team.prezel.core.designsystem.component.button.PrezelButton -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.component.actions.button.PrezelButton +import com.team.prezel.core.designsystem.preview.BasicPreview import com.team.prezel.core.designsystem.theme.PrezelTheme import com.team.prezel.feature.login.api.AUTH_LOGO_SHARED_ELEMENT_KEY import com.team.prezel.feature.login.impl.viewModel.LoginUiEffect @@ -145,7 +145,7 @@ private fun LoginFooter( } } -@ThemePreview +@BasicPreview @Composable private fun LoginScreenPreview() { PrezelTheme { diff --git a/Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt b/Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt index 347130e1..745416db 100644 --- a/Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt +++ b/Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import com.team.prezel.core.designsystem.preview.ThemePreview +import com.team.prezel.core.designsystem.preview.BasicPreview import com.team.prezel.core.designsystem.theme.PrezelTheme import com.team.prezel.feature.login.api.AUTH_LOGO_SHARED_ELEMENT_KEY import com.team.prezel.feature.splash.impl.viewModel.SplashUiEffect @@ -72,7 +72,7 @@ private fun SharedTransitionScope.SplashScreen( } } -@ThemePreview +@BasicPreview @Composable private fun SplashScreenPreview() { PrezelTheme {