diff --git a/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuDialog.kt b/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuDialog.kt new file mode 100644 index 000000000..81a541b3f --- /dev/null +++ b/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuDialog.kt @@ -0,0 +1,240 @@ +package com.eatssu.design_system.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.BasicAlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.eatssu.design_system.theme.Black +import com.eatssu.design_system.theme.Danger +import com.eatssu.design_system.theme.EatssuTheme +import com.eatssu.design_system.theme.Gray200 +import com.eatssu.design_system.theme.Gray600 +import com.eatssu.design_system.theme.White + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun EatSsuDialog( + title: String, + description: String, + confirmText: String, + onConfirmClick: () -> Unit, + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, + dismissText: String? = null, + onDismissButtonClick: (() -> Unit)? = null, + icon: Painter? = null, +) { + BasicAlertDialog( + onDismissRequest = onDismissRequest, + ) { + Surface( + modifier = modifier, + shape = RoundedCornerShape(28.dp), + color = White + ) { + Column( + modifier = Modifier + .padding(horizontal = 18.dp, vertical = 18.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = title, + style = EatssuTheme.typography.h2, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = description, + style = EatssuTheme.typography.subtitle2, + color = Gray600 + ) + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + if (!dismissText.isNullOrBlank()) { + Button( + modifier = Modifier.weight(1f), + onClick = onConfirmClick, + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Gray200, + contentColor = Black + ) + ) { + Text( + text = dismissText, + style = EatssuTheme.typography.button2 + ) + } + } + + Button( + modifier = Modifier.weight(1f), + onClick = onConfirmClick, + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = Color.White + ) + ) { + Text( + text = confirmText, + style = EatssuTheme.typography.button2 + ) + } + } + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun EatSsuWarningDialog( + title: String, + description: String, + confirmText: String, + dismissText: String, + onConfirmClick: () -> Unit, + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, + onDismissButtonClick: (() -> Unit)? = null, + icon: Painter? = null, +) { + BasicAlertDialog( + onDismissRequest = onDismissRequest, + ) { + Surface( + modifier = modifier, + shape = RoundedCornerShape(28.dp), + color = White + ) { + Column( + modifier = Modifier + .padding(horizontal = 18.dp, vertical = 18.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = title, + style = EatssuTheme.typography.h2, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = description, + style = EatssuTheme.typography.subtitle2, + color = Gray600 + ) + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Button( + modifier = Modifier.weight(1f), + onClick = onConfirmClick, + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Danger, + contentColor = White + ) + ) { + Text( + text = confirmText, + style = EatssuTheme.typography.button2 + ) + } + + Button( + modifier = Modifier.weight(1f), + onClick = onConfirmClick, + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Gray200, + contentColor = Black + ) + ) { + Text( + text = dismissText, + style = EatssuTheme.typography.button2 + ) + } + } + } + } + } +} + +@Preview +@Composable +private fun EatSsuDialogPreview() { + EatssuTheme { + EatSsuDialog( + title = "리뷰를 삭제하시겠어요?", + description = "삭제한 리뷰는 다시 복구할 수 없습니다.", + confirmText = "확인", + dismissText = "취소", + onConfirmClick = {}, + onDismissRequest = {} + ) + } +} + +@Preview +@Composable +private fun EatSsuDialog1Preview() { + EatssuTheme { + EatSsuDialog( + title = "리뷰를 삭제하시겠어요?", + description = "삭제한 리뷰는 다시 복구할 수 없습니다.", + confirmText = "확인", + onConfirmClick = {}, + onDismissRequest = {} + ) + } +} + +@Preview +@Composable +private fun EatSsuDangerDialogPreview() { + EatssuTheme { + EatSsuWarningDialog( + title = "리뷰를 삭제하시겠어요?", + description = "삭제한 리뷰는 다시 복구할 수 없습니다.", + confirmText = "확인", + dismissText = "취소", + onConfirmClick = {}, + onDismissRequest = {} + ) + } +} diff --git a/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuSnackbar.kt b/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuSnackbar.kt new file mode 100644 index 000000000..b494d1aeb --- /dev/null +++ b/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuSnackbar.kt @@ -0,0 +1,185 @@ +package com.eatssu.design_system.component + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Snackbar +import androidx.compose.material3.SnackbarData +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarVisuals +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +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.graphics.painter.Painter +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.eatssu.design_system.theme.Danger +import com.eatssu.design_system.theme.DangerBg +import com.eatssu.design_system.theme.DangerBr +import com.eatssu.design_system.theme.EatssuTheme +import com.eatssu.design_system.theme.Gray600 +import com.eatssu.design_system.theme.Info +import com.eatssu.design_system.theme.InfoBg +import com.eatssu.design_system.theme.InfoBr +import com.eatssu.design_system.theme.Success +import com.eatssu.design_system.theme.SuccessBg +import com.eatssu.design_system.theme.SuccessBr +import com.eatssu.design_system.theme.Warning +import com.eatssu.design_system.theme.WarningBg +import com.eatssu.design_system.theme.WarningBr + +enum class EatSsuSnackbarType { + Success, Danger, Info, Warning +} + +private data class EatSsuSnackbarColor( + val container: Color, + val stroke: Color, + val action: Color +) + +private fun EatSsuSnackbarType.colors(): EatSsuSnackbarColor = when (this) { + EatSsuSnackbarType.Success -> EatSsuSnackbarColor( + container = SuccessBg, + stroke = SuccessBr, + action = Success + ) + + EatSsuSnackbarType.Danger -> EatSsuSnackbarColor( + container = DangerBg, + stroke = DangerBr, + action = Danger + ) + + EatSsuSnackbarType.Info -> EatSsuSnackbarColor( + container = InfoBg, + stroke = InfoBr, + action = Info + ) + + EatSsuSnackbarType.Warning -> EatSsuSnackbarColor( + container = WarningBg, + stroke = WarningBr, + action = Warning + ) +} + +@Composable +fun EatSsuSnackbarHost( + hostState: SnackbarHostState, + modifier: Modifier = Modifier, + icon: Painter? = null, + type: EatSsuSnackbarType = EatSsuSnackbarType.Info, +) { + SnackbarHost( + hostState = hostState, + modifier = modifier, + ) { snackbarData -> + EatSsuSnackbar( + snackbarData = snackbarData, + icon = icon, + type = type, + ) + } +} + +@Composable +fun EatSsuSnackbar( + snackbarData: SnackbarData, + modifier: Modifier = Modifier, + icon: Painter? = null, + type: EatSsuSnackbarType = EatSsuSnackbarType.Info, +) { + val colors = type.colors() + + Surface( + shape = RoundedCornerShape(12.dp), + border = BorderStroke(1.dp, colors.stroke), + color = Color.Transparent, + modifier = modifier.padding(vertical = 14.dp, horizontal = 16.dp) + ) { + Snackbar( + containerColor = colors.container, + shape = RoundedCornerShape(12.dp), + action = { + val actionLabel = snackbarData.visuals.actionLabel + if (!actionLabel.isNullOrBlank()) { + TextButton(onClick = { snackbarData.performAction() }) { + Text( + text = actionLabel, + style = EatssuTheme.typography.button2.copy( + textDecoration = TextDecoration.Underline + ), + color = colors.action + ) + } + } + } + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + if (icon != null) { + androidx.compose.material3.Icon( + painter = icon, + contentDescription = null, + ) + } + + Text( + text = snackbarData.visuals.message, + modifier = Modifier.weight(1f), + style = EatssuTheme.typography.body1, + color = Gray600, + maxLines = 2, + overflow = TextOverflow.Ellipsis + ) + } + } + } +} + +private class PreviewSnackbarData( + message: String, + actionLabel: String? = null +) : SnackbarData { + private val previewVisuals = object : SnackbarVisuals { + override val message: String = message + override val actionLabel: String? = actionLabel + override val duration = SnackbarDuration.Short + override val withDismissAction: Boolean = actionLabel != null + } + + override val visuals: SnackbarVisuals + get() = previewVisuals + + override fun dismiss() {} + override fun performAction() {} +} + +@Preview(showBackground = true) +@Composable +private fun EatSsuSnackbarPreview() { + EatssuTheme { + EatSsuSnackbar( + snackbarData = PreviewSnackbarData( + message = "리뷰가 등록되었어요.", + actionLabel = "취소" + ), + modifier = Modifier.padding(16.dp), + type = EatSsuSnackbarType.Success + ) + } +} + diff --git a/core/design-system/src/main/java/com/eatssu/design_system/theme/Color.kt b/core/design-system/src/main/java/com/eatssu/design_system/theme/Color.kt index bd754fb53..11589d2ac 100644 --- a/core/design-system/src/main/java/com/eatssu/design_system/theme/Color.kt +++ b/core/design-system/src/main/java/com/eatssu/design_system/theme/Color.kt @@ -16,9 +16,29 @@ val Gray700 = Color(0xFF1F1F1F) // Brand val Primary = Color(0xFF66D4C2) val Secondary = Color(0xFFEEFBF8) + +// Yellow val Star = Color(0xFFFFC700) + +// Red val Error = Color(0xFFFF3F3F) // Special val KakaoLogin = Color(0xFFFEE500) +// Alert +val Info = Color(0xFF3D69FF) +val InfoBg = Color(0xFFE7F4FE) +val InfoBr = Color(0xFFD3EBFD) + +val Success = Color(0xFF228738) +val SuccessBg = Color(0xFFEAF6EC) +val SuccessBr = Color(0xFFD8EEDD) + +val Warning = Color(0xFFD17D00) +val WarningBg = Color(0xFFFFF3DB) +val WarningBr = Color(0xFFFFE0A3) + +val Danger = Color(0xFFDE3412) +val DangerBg = Color(0xFFFDEFEC) +val DangerBr = Color(0xFFFCDFD9) \ No newline at end of file