diff --git a/.coderabbit.yaml b/.coderabbit.yaml index cb12ae0b..63ab66d9 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -1,13 +1,17 @@ # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json language: ko-KR + reviews: - collapse_walkthrough: false - changed_files_summary: false + profile: chill + high_level_summary: true + changed_files_summary: true + collapse_walkthrough: true sequence_diagrams: false estimate_code_review_effort: false suggested_reviewers: false in_progress_fortune: false poem: false + finishing_touches: docstrings: enabled: false 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 deleted file mode 100644 index 86858124..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DatePickerDayCellView.kt +++ /dev/null @@ -1,100 +0,0 @@ -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.RowScope -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.CircleShape -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.graphics.Color -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 - -@Composable -internal fun RowScope.DayCellView( - uiModel: DayCell?, - onClick: () -> Unit, -) { - Box( - modifier = Modifier - .weight(1f) - .aspectRatio(1f) - .padding(PrezelTheme.spacing.V4) - .clip(CircleShape) - .background( - if (uiModel?.isSelected == true) { - PrezelTheme.colors.interactiveRegular - } else { - Color.Transparent - }, - ).clickable( - indication = ripple(), - interactionSource = null, - enabled = uiModel?.isVisible == true, - onClick = onClick, - ), - contentAlignment = Alignment.Center, - ) { - if (uiModel == null) return@Box - if (!uiModel.isVisible) return@Box - - Text( - text = uiModel.dayText, - color = uiModel.dayTextColor(), - style = if (uiModel.isSelected) { - PrezelTheme.typography.body3Bold - } else { - PrezelTheme.typography.body3Medium - }, - ) - } -} - -@BasicPreview -@Composable -private fun DayCellViewPreview() { - PreviewSection( - title = "DatePicker/Day", - description = "DatePicker에 사용되는 리소스입니다.", - ) { - PreviewRow { - DayCellView( - uiModel = DayCell( - date = LocalDate(2024, 1, 1), - isSelected = false, - isToday = false, - isVisible = true, - ), - onClick = {}, - ) - DayCellView( - uiModel = DayCell( - date = LocalDate(2024, 1, 2), - isSelected = true, - isToday = false, - isVisible = true, - ), - onClick = {}, - ) - DayCellView( - uiModel = DayCell( - date = LocalDate(2024, 1, 3), - isSelected = false, - isToday = true, - isVisible = true, - ), - onClick = {}, - ) - } - } -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DatePickerStyle.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DatePickerStyle.kt deleted file mode 100644 index ebffaafc..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DatePickerStyle.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.team.prezel.core.designsystem.component.datepicker - -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import com.team.prezel.core.designsystem.theme.PrezelTheme - -@Composable -internal fun DayCell.dayTextColor(): Color = - when { - this.isSelected -> PrezelTheme.colors.bgRegular - this.isToday -> PrezelTheme.colors.interactiveRegular - this.isSunday -> PrezelTheme.colors.accentMagentaRegular - else -> PrezelTheme.colors.textMedium - } diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DayCell.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DayCell.kt deleted file mode 100644 index fce69a12..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/DayCell.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.team.prezel.core.designsystem.component.datepicker - -import androidx.compose.runtime.Immutable -import kotlinx.datetime.DayOfWeek -import kotlinx.datetime.LocalDate - -@Immutable -internal data class DayCell( - val date: LocalDate, - val isSelected: Boolean, - val isToday: Boolean, - val isVisible: Boolean, -) { - val dayText: String = date.day.toString() - val isSunday: Boolean = date.dayOfWeek == DayOfWeek.SUNDAY -} 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 deleted file mode 100644 index 74241f9a..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthGrid.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.team.prezel.core.designsystem.component.datepicker - -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.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -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 -import kotlinx.datetime.LocalDate -import kotlinx.datetime.YearMonth - -@Composable -internal fun MonthGrid( - yearMonth: YearMonth, - selectedDate: LocalDate?, - today: LocalDate, - onSelect: (LocalDate) -> Unit, -) { - val (cells, lastWeek) = remember(yearMonth) { - val c = buildMonthGrid(yearMonth = yearMonth, firstDayOfWeek = DayOfWeek.SUNDAY) - c to lastWeekIndexToRender(c) - } - - Column(modifier = Modifier.padding(top = PrezelTheme.spacing.V16)) { - for (week in 0..lastWeek) { - WeekRow(cells = cells, week = week, selectedDate = selectedDate, today = today, onSelect = onSelect) - } - } -} - -@Composable -private fun WeekRow( - cells: ImmutableList, - week: Int, - selectedDate: LocalDate?, - today: LocalDate, - onSelect: (LocalDate) -> Unit, -) { - Row(modifier = Modifier.fillMaxWidth()) { - for (day in 0 until 7) { - val date = cells[week * 7 + day] - - if (date == null) { - DayCellView( - uiModel = null, - ) { } - continue - } - - val isPast = date < today - val uiModel = DayCell( - date = date, - isSelected = date == selectedDate, - isToday = date == today, - isVisible = !isPast, - ) - - DayCellView( - uiModel = uiModel, - onClick = { onSelect(date) }, - ) - } - } -} - -@BasicPreview -@Composable -private fun MonthGridPreview() { - PreviewSection( - title = "DatePicker/MonthGrid", - description = "DatePicker에 사용되는 리소스입니다.", - ) { - MonthGrid( - yearMonth = YearMonth(year = 2026, month = 2), - selectedDate = LocalDate(year = 2026, month = 2, day = 26), - today = LocalDate(year = 2026, month = 2, day = 25), - onSelect = {}, - ) - } -} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthGridBuilder.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthGridBuilder.kt deleted file mode 100644 index 64e2ac4c..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthGridBuilder.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.team.prezel.core.designsystem.component.datepicker - -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList -import kotlinx.datetime.DayOfWeek -import kotlinx.datetime.LocalDate -import kotlinx.datetime.YearMonth -import kotlinx.datetime.isoDayNumber -import kotlinx.datetime.onDay - -internal fun buildMonthGrid( - yearMonth: YearMonth, - firstDayOfWeek: DayOfWeek, -): ImmutableList { - val firstOfMonth = yearMonth.onDay(1) - val lastDay = yearMonth.numberOfDays - - val shift = ((firstOfMonth.dayOfWeek.isoDayNumber - firstDayOfWeek.isoDayNumber) + 7) % 7 - val totalCells = 42 - - return (0 until totalCells) - .map { index -> - val dayNumber = index - shift + 1 - if (dayNumber in 1..lastDay) { - yearMonth.onDay(dayNumber) - } else { - null - } - }.toPersistentList() -} - -internal fun lastWeekIndexToRender(cells: List): Int { - // 마지막으로 실제 날짜가 존재하는 셀 인덱스 (0..41) - val last = cells.indexOfLast { it != null } - // month가 비정상일 경우 방어 - if (last < 0) return 0 - - // 주 단위로 올림 → 마지막 날짜가 포함된 주 index (0..5) - return (last / 7).coerceIn(0, 5) -} 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 deleted file mode 100644 index 8c553221..00000000 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/MonthSection.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.team.prezel.core.designsystem.component.datepicker - -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.res.stringResource -import com.team.prezel.core.designsystem.R -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 -import kotlinx.datetime.number - -@Composable -internal fun MonthSection( - yearMonth: YearMonth, - selectedDate: LocalDate?, - today: LocalDate, - onSelect: (LocalDate) -> Unit, -) { - Column( - modifier = Modifier.padding(PrezelTheme.spacing.V20), - ) { - Text( - text = stringResource( - id = R.string.core_designsystem_date_picker_month_title, - yearMonth.year, - yearMonth.month.number, - ), - color = PrezelTheme.colors.textLarge, - style = PrezelTheme.typography.body3Medium, - ) - - MonthGrid( - yearMonth = yearMonth, - selectedDate = selectedDate, - today = today, - onSelect = onSelect, - ) - } -} - -@BasicPreview -@Composable -private fun MonthSectionPreview() { - PreviewSection( - title = "Month Section", - description = "DatePicker에 사용되는 리소스입니다.", - ) { - MonthSection( - yearMonth = YearMonth(year = 2026, month = 2), - selectedDate = LocalDate(year = 2026, month = 2, day = 26), - today = LocalDate(year = 2026, month = 2, day = 25), - onSelect = {}, - ) - } -} 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 519c1fcd..0d07bb72 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 @@ -1,10 +1,12 @@ package com.team.prezel.core.designsystem.component.datepicker 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.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -31,6 +33,9 @@ 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.actions.area.PrezelButtonArea +import com.team.prezel.core.designsystem.component.datepicker.config.DatePickerDefault +import com.team.prezel.core.designsystem.component.datepicker.config.DatePickerDefaults +import com.team.prezel.core.designsystem.component.datepicker.config.DatePickerMonth import com.team.prezel.core.designsystem.icon.PrezelIcons import com.team.prezel.core.designsystem.preview.PreviewDefaults import com.team.prezel.core.designsystem.preview.PreviewSurface @@ -46,43 +51,44 @@ import kotlin.time.Clock @Composable fun PrezelDatePicker( title: String, - selectedDate: LocalDate?, - onSelect: (LocalDate) -> Unit, onClose: () -> Unit, onConfirm: (LocalDate) -> Unit, modifier: Modifier = Modifier, today: LocalDate = Clock.System.todayIn(TimeZone.currentSystemDefault()), + initialSelectedDate: LocalDate? = null, + config: DatePickerDefault = DatePickerDefaults.default(), ) { - val initialMonth = remember(today) { YearMonth(today.year, today.month) } - val months = remember(initialMonth) { + var selectedDate by remember(initialSelectedDate) { mutableStateOf(initialSelectedDate) } + + val months = remember(today) { List(12) { offset -> - initialMonth.plus(value = offset, unit = DateTimeUnit.MONTH) + YearMonth(today.year, today.month).plus(value = offset, unit = DateTimeUnit.MONTH) } } Column( modifier = modifier .fillMaxSize() - .background(PrezelTheme.colors.bgRegular), + .background(color = config.containerColor), ) { DatePickerHeader(title = title, onClose = onClose) - LazyColumn( - modifier = Modifier.weight(1f), - contentPadding = PaddingValues(bottom = PrezelTheme.spacing.V16), - overscrollEffect = null, - ) { - items(items = months, key = { it.toString() }) { month -> - MonthSection( + LazyColumn(modifier = Modifier.weight(1f)) { + items(items = months, key = { month -> month }) { month -> + DatePickerMonth( yearMonth = month, selectedDate = selectedDate, today = today, - onSelect = onSelect, + onSelect = { date -> selectedDate = date }, + config = config, ) } } - DatePickerFooter(selectedDate = selectedDate, onConfirm = onConfirm) + DatePickerFooter( + enabled = initialSelectedDate != selectedDate, + onClick = { selectedDate?.let(onConfirm) }, + ) } } @@ -111,8 +117,8 @@ private fun DatePickerHeader( @Composable private fun DatePickerFooter( - selectedDate: LocalDate?, - onConfirm: (LocalDate) -> Unit, + enabled: Boolean, + onClick: () -> Unit, ) { val buttonLabel = stringResource(R.string.core_designsystem_date_picker_confirm_btn) @@ -122,8 +128,8 @@ private fun DatePickerFooter( ) { MainButton( label = buttonLabel, - enabled = selectedDate != null, - onClick = { selectedDate?.let(onConfirm) }, + enabled = enabled, + onClick = onClick, ) } } @@ -135,14 +141,15 @@ private fun WeekdayRow() { Row( modifier = Modifier .fillMaxWidth() - .padding( - horizontal = PrezelTheme.spacing.V20, - vertical = PrezelTheme.spacing.V12, - ), + .padding(horizontal = PrezelTheme.spacing.V20), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(PrezelTheme.spacing.V2), ) { labels.forEach { text -> Box( - modifier = Modifier.weight(1f), + modifier = Modifier + .weight(1f) + .aspectRatio(1f), contentAlignment = Alignment.Center, ) { Text( @@ -158,18 +165,13 @@ private fun WeekdayRow() { @Preview(showBackground = true) @Composable private fun PrezelDatePickerPreview() { - 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), - selectedDate = selected, - onSelect = { selected = it }, + today = LocalDate(year = 2026, month = 3, day = 16), + initialSelectedDate = LocalDate(year = 2026, month = 3, day = 25), onClose = {}, onConfirm = {}, ) diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DatePickerDefaults.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DatePickerDefaults.kt new file mode 100644 index 00000000..86701a04 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DatePickerDefaults.kt @@ -0,0 +1,55 @@ +package com.team.prezel.core.designsystem.component.datepicker.config + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import com.team.prezel.core.designsystem.theme.PrezelColorScheme +import com.team.prezel.core.designsystem.theme.PrezelTheme + +@Immutable +data class DatePickerDefault( + val containerColor: Color, + private val selectedDayBackgroundColor: Color, + private val unselectedDayBackgroundColor: Color, + private val selectedDayTextStyle: TextStyle, + private val unselectedDayTextStyle: TextStyle, + private val selectedDayTextColor: Color, + private val dayTextColor: Color, + private val pastDayTextColor: Color, + private val todayDayTextColor: Color, + private val holidayDayTextColor: Color, +) { + internal fun dayContainerColor(dayCell: DayCellType?): Color = + if (dayCell?.isSelected == true) selectedDayBackgroundColor else unselectedDayBackgroundColor + + internal fun dayTextStyle(dayCell: DayCellType): TextStyle = if (dayCell.isSelected) selectedDayTextStyle else unselectedDayTextStyle + + internal fun dayTextColor(dayCell: DayCellType): Color { + if (dayCell.isSelected) return selectedDayTextColor + + return when (dayCell) { + is DayCellType.Default -> dayTextColor + is DayCellType.Past -> pastDayTextColor + is DayCellType.Today -> todayDayTextColor + is DayCellType.Holiday -> holidayDayTextColor + } + } +} + +internal object DatePickerDefaults { + @Composable + fun default(): DatePickerDefault = + DatePickerDefault( + containerColor = PrezelTheme.colors.bgRegular, + selectedDayBackgroundColor = PrezelTheme.colors.interactiveRegular, + unselectedDayBackgroundColor = Color.Transparent, + selectedDayTextStyle = PrezelTheme.typography.body3Bold, + unselectedDayTextStyle = PrezelTheme.typography.body3Medium, + selectedDayTextColor = PrezelColorScheme.Dark.textLarge, + dayTextColor = PrezelTheme.colors.textMedium, + pastDayTextColor = Color.Transparent, + todayDayTextColor = PrezelTheme.colors.interactiveRegular, + holidayDayTextColor = PrezelTheme.colors.accentMagentaRegular, + ) +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DatePickerMonth.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DatePickerMonth.kt new file mode 100644 index 00000000..0c7665a0 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DatePickerMonth.kt @@ -0,0 +1,132 @@ +package com.team.prezel.core.designsystem.component.datepicker.config + +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.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +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.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 +import kotlinx.datetime.isoDayNumber +import kotlinx.datetime.number +import kotlinx.datetime.onDay + +@Composable +internal fun DatePickerMonth( + yearMonth: YearMonth, + selectedDate: LocalDate?, + today: LocalDate, + onSelect: (LocalDate) -> Unit, + config: DatePickerDefault, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier.padding(PrezelTheme.spacing.V20)) { + MonthHeader(yearMonth = yearMonth) + + Spacer(modifier = Modifier.height(PrezelTheme.spacing.V16)) + + MonthGrid( + yearMonth = yearMonth, + selectedDate = selectedDate, + today = today, + onSelect = onSelect, + config = config, + ) + } +} + +@Composable +private fun MonthHeader( + yearMonth: YearMonth, + modifier: Modifier = Modifier, +) { + Text( + text = stringResource( + id = R.string.core_designsystem_date_picker_month_title, + yearMonth.year, + yearMonth.month.number, + ), + color = PrezelTheme.colors.textLarge, + style = PrezelTheme.typography.body3Medium, + modifier = modifier, + ) +} + +@Composable +private fun MonthGrid( + yearMonth: YearMonth, + selectedDate: LocalDate?, + today: LocalDate, + onSelect: (LocalDate) -> Unit, + config: DatePickerDefault, + modifier: Modifier = Modifier, +) { + val weeks = remember(yearMonth, selectedDate, today) { + buildMonthGrid(yearMonth) + .map { date -> + if (date == null) return@map null + DayCellType.from(date = date, selectedDate = selectedDate, today = today) + }.chunked(7) + .filter { week -> week.any { cell -> cell != null && cell !is DayCellType.Past } } + } + + Column( + modifier = modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(PrezelTheme.spacing.V2), + ) { + weeks.forEach { week -> + Row(modifier = Modifier.fillMaxWidth()) { + week.forEach { cell -> + DayCell( + dayCell = cell, + config = config, + onClick = { cell?.let { onSelect(it.date) } }, + modifier = Modifier.weight(1f), + ) + } + } + } + } +} + +private fun buildMonthGrid(yearMonth: YearMonth): List { + val firstDate = yearMonth.onDay(1) + val lastDay = yearMonth.numberOfDays + val leadingEmptyCount = firstDate.dayOfWeek.isoDayNumber % 7 + + return (0 until 42) + .map { index -> + val dayNumber = index - leadingEmptyCount + 1 + if (dayNumber !in 1..lastDay) return@map null + + yearMonth.onDay(dayNumber) + } +} + +@BasicPreview +@Composable +private fun DatePickerMonthPreview() { + PreviewSection( + title = "Month Section", + description = "DatePicker에 사용되는 리소스입니다.", + ) { + DatePickerMonth( + yearMonth = YearMonth(year = 2026, month = 2), + selectedDate = LocalDate(year = 2026, month = 2, day = 28), + today = LocalDate(year = 2026, month = 2, day = 18), + onSelect = {}, + config = DatePickerDefaults.default(), + ) + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DayCell.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DayCell.kt new file mode 100644 index 00000000..f0e30e1d --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DayCell.kt @@ -0,0 +1,103 @@ +package com.team.prezel.core.designsystem.component.datepicker.config + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +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.component.base.PrezelTouchArea +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.datetime.LocalDate + +@Composable +internal fun DayCell( + dayCell: DayCellType?, + config: DatePickerDefault, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + PrezelTouchArea( + enabled = dayCell != null && dayCell !is DayCellType.Past, + modifier = modifier + .aspectRatio(1f) + .padding(PrezelTheme.spacing.V4) + .clip(PrezelTheme.shapes.V1000) + .background(color = config.dayContainerColor(dayCell = dayCell)), + onClick = onClick, + ) { + if (dayCell == null) return@PrezelTouchArea + + Text( + text = dayCell.date.day.toString(), + color = config.dayTextColor(dayCell = dayCell), + style = config.dayTextStyle(dayCell = dayCell), + ) + } +} + +@BasicPreview +@Composable +private fun DayCellPreview() { + PreviewSection( + title = "DatePicker/Day", + description = "DatePicker에 사용되는 리소스입니다.", + ) { + PreviewValueRow(name = "Default") { + Box(modifier = Modifier.size(50.dp)) { + DayCell( + dayCell = DayCellType.Default(LocalDate(year = 2026, month = 2, day = 26), isSelected = false), + config = DatePickerDefaults.default(), + onClick = {}, + ) + } + } + + PreviewValueRow(name = "Past") { + Box(modifier = Modifier.size(50.dp)) { + DayCell( + dayCell = DayCellType.Past(LocalDate(year = 2026, month = 2, day = 26), isSelected = false), + config = DatePickerDefaults.default(), + onClick = {}, + ) + } + } + + PreviewValueRow(name = "Today") { + Box(modifier = Modifier.size(50.dp)) { + DayCell( + dayCell = DayCellType.Today(LocalDate(year = 2026, month = 2, day = 26), isSelected = false), + config = DatePickerDefaults.default(), + onClick = {}, + ) + } + } + + PreviewValueRow(name = "Holiday") { + Box(modifier = Modifier.size(50.dp)) { + DayCell( + dayCell = DayCellType.Holiday(LocalDate(year = 2026, month = 2, day = 26), isSelected = false), + config = DatePickerDefaults.default(), + onClick = {}, + ) + } + } + + PreviewValueRow(name = "Selected") { + Box(modifier = Modifier.size(50.dp)) { + DayCell( + dayCell = DayCellType.Default(LocalDate(year = 2026, month = 2, day = 26), isSelected = true), + config = DatePickerDefaults.default(), + onClick = {}, + ) + } + } + } +} diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DayCellType.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DayCellType.kt new file mode 100644 index 00000000..4a5a3065 --- /dev/null +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DayCellType.kt @@ -0,0 +1,52 @@ +package com.team.prezel.core.designsystem.component.datepicker.config + +import androidx.compose.runtime.Immutable +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.LocalDate + +@Immutable +internal sealed interface DayCellType { + val date: LocalDate + val isSelected: Boolean + + data class Default( + override val date: LocalDate, + override val isSelected: Boolean, + ) : DayCellType + + data class Past( + override val date: LocalDate, + override val isSelected: Boolean, + ) : DayCellType + + data class Today( + override val date: LocalDate, + override val isSelected: Boolean, + ) : DayCellType + + data class Holiday( + override val date: LocalDate, + override val isSelected: Boolean, + ) : DayCellType { + companion object { + fun isHoliday(date: LocalDate): Boolean = date.dayOfWeek == DayOfWeek.SUNDAY + } + } + + companion object { + fun from( + date: LocalDate, + selectedDate: LocalDate?, + today: LocalDate, + ): DayCellType { + val isSelected = date == selectedDate + + return when { + date < today -> Past(date = date, isSelected = isSelected) + date == today -> Today(date = date, isSelected = isSelected) + Holiday.isHoliday(date = date) -> Holiday(date = date, isSelected = isSelected) + else -> Default(date = date, isSelected = isSelected) + } + } + } +}