Skip to content

Commit

Permalink
#18 [FIX] 시간행 크기 고정
Browse files Browse the repository at this point in the history
  • Loading branch information
gaeulzzang committed Jan 9, 2025
1 parent de95476 commit 48842ad
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 157 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import androidx.compose.ui.unit.dp
import com.sopt.core.designsystem.theme.NoostakTheme
import com.sopt.core.extension.noRippleClickable
import com.sopt.core.type.CellType
import com.sopt.core.util.timetable.TimeTable
import com.sopt.domain.entity.AvailableTimeEntity
import com.sopt.domain.entity.TimeEntity
import com.sopt.domain.entity.TimeTableEntity
import java.sql.Time

@Composable
fun NoostakEditableTimeTable(
Expand All @@ -31,7 +33,7 @@ fun NoostakEditableTimeTable(
onSelectionChange: (List<TimeEntity>) -> Unit
) {
val days = data.timeEntity.size
val timeSlots = calculateTimeSlots(data.startTime, data.endTime)
val timeSlots = TimeTable().calculateTimeSlots(data.startTime, data.endTime)
val selectedCells = remember { mutableStateListOf<Pair<Int, Int>>() } // Row, Column 저장

LazyVerticalGrid(
Expand All @@ -45,11 +47,11 @@ fun NoostakEditableTimeTable(
) {
items((days + 1) * (timeSlots + 1)) { index ->
val (rowIndex, columnIndex) = index / (days + 1) to index % (days + 1)
val cellType = determineCellType(rowIndex, columnIndex)
val cellType = TimeTable().determineCellType(rowIndex, columnIndex)
val isSelected = selectedCells.contains(rowIndex to columnIndex)
val backgroundColor =
getEditableBackgroundColor(cellType, isSelected)
val text = getCellText(cellType, rowIndex, columnIndex, data)
val text = TimeTable().getCellText(cellType, rowIndex, columnIndex, data)

NoostakEditableTimeTableBox(
index = index,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,186 +3,128 @@ package com.sopt.core.designsystem.component.timetable
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
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
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.sopt.core.designsystem.theme.Gray200
import com.sopt.core.designsystem.theme.NoostakAndroidTheme
import com.sopt.core.designsystem.theme.NoostakTheme
import com.sopt.core.type.AvailabilityLevel
import com.sopt.core.type.CellType
import com.sopt.core.util.timetable.TimeTable
import com.sopt.domain.entity.AvailableTimeEntity
import com.sopt.domain.entity.TimeEntity
import com.sopt.domain.entity.TimeTableEntity
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.time.format.TextStyle
import java.util.Locale

@Composable
fun NoostakTimeTable(
data: TimeTableEntity,
modifier: Modifier = Modifier
) {
val days = data.timeEntity.size
val timeSlots = calculateTimeSlots(data.startTime, data.endTime)
val timeSlots = TimeTable().calculateTimeSlots(data.startTime, data.endTime)

LazyVerticalGrid(
modifier = modifier,
columns = GridCells.Fixed(days + 1),
contentPadding = PaddingValues(0.dp)
) {
items((days + 1) * (timeSlots + 1)) { index ->
val (rowIndex, columnIndex) = index / (days + 1) to index % (days + 1)

val cellType = determineCellType(rowIndex, columnIndex)
val backgroundColor = getBackgroundColor(cellType, rowIndex, columnIndex, data)
val text = getCellText(cellType, rowIndex, columnIndex, data)

NoostakTimeTableBox(
index = index,
days = days,
timeSlots = timeSlots,
backgroundColor = backgroundColor,
text = text
)
}
}
}

@Composable
fun getBackgroundColor(
cellType: CellType,
rowIndex: Int,
columnIndex: Int,
data: TimeTableEntity
): Color = when (cellType) {
CellType.Blank, CellType.DateHeader, CellType.TimeHeader -> Color.Transparent
CellType.Data -> {
val startHour = data.startTime.split(":")[0].toInt()
val currentHour = startHour + (rowIndex - 1)
val availableTimes = data.timeEntity.getOrNull(columnIndex - 1)?.times

val matchingLevel = availableTimes?.find { timeEntity ->
val entityStartHour = timeEntity.startTime.split(":")[0].toInt()
val entityEndHour = timeEntity.endTime.split(":")[0].toInt()
currentHour in entityStartHour until entityEndHour
}?.level

getColorByLevel(matchingLevel ?: 0)
}
}

fun getCellText(
cellType: CellType,
rowIndex: Int,
columnIndex: Int,
data: TimeTableEntity
): String {
val startHour = data.startTime.split(":")[0].toInt()

return when (cellType) {
CellType.Blank -> "\n"
CellType.DateHeader -> {
val dateEntity = data.timeEntity.getOrNull(columnIndex - 1)
val date = dateEntity?.date ?: ""
formatDateHeader(date)
}

CellType.TimeHeader -> "${startHour + (rowIndex - 1)}"
CellType.Data -> ""
}
}

@Composable
fun NoostakTimeTableBox(
index: Int,
days: Int,
timeSlots: Int,
backgroundColor: Color,
text: String
) {
val shape = when (index) {
0 -> RoundedCornerShape(topStart = 10.dp)
days -> RoundedCornerShape(topEnd = 10.dp)
(days + 1) * timeSlots -> RoundedCornerShape(bottomStart = 10.dp)
(days + 1) * (timeSlots + 1) - 1 -> RoundedCornerShape(bottomEnd = 10.dp)
else -> RoundedCornerShape(0.dp)
}

Box(
modifier = Modifier
LazyColumn(
modifier = modifier
.border(
width = 0.5.dp,
color = NoostakTheme.colors.gray100,
shape = shape
width = 1.dp,
color = NoostakTheme.colors.gray200,
shape = RoundedCornerShape(8.dp)
)
.background(
color = backgroundColor,
shape = shape
)
.defaultMinSize(minWidth = 42.dp, minHeight = 36.dp),
contentAlignment = Alignment.Center
) {
Text(
modifier = Modifier.padding(horizontal = 5.dp, vertical = 3.dp),
text = text,
color = NoostakTheme.colors.gray600,
style = NoostakTheme.typography.c4Regular,
textAlign = TextAlign.Center,
maxLines = 2
)
}
}

fun determineCellType(rowIndex: Int, columnIndex: Int): CellType = when {
rowIndex == 0 && columnIndex == 0 -> CellType.Blank
rowIndex == 0 -> CellType.DateHeader
columnIndex == 0 -> CellType.TimeHeader
else -> CellType.Data
}

@Composable
fun getColorByLevel(level: Int): Color =
when (AvailabilityLevel.entries.firstOrNull { level in it.range }) {
AvailabilityLevel.NONE -> Color.Transparent
AvailabilityLevel.FEW -> NoostakTheme.colors.blue50
AvailabilityLevel.SOME -> NoostakTheme.colors.blue200
AvailabilityLevel.MANY -> NoostakTheme.colors.blue400
AvailabilityLevel.MOST -> NoostakTheme.colors.blue700
else -> NoostakTheme.colors.blue800
// 행 반복
items(timeSlots + 1) { rowIndex ->
Row(modifier = Modifier.fillMaxWidth()) {
// 열 반복
for (columnIndex in 0..days) {
val cellType = TimeTable().determineCellType(rowIndex, columnIndex)
val backgroundColor = TimeTable().getBackgroundColor(cellType, rowIndex, columnIndex, data)
val text = TimeTable().getCellText(cellType, rowIndex, columnIndex, data)
val shape = when (rowIndex to columnIndex) {
0 to 0 -> RoundedCornerShape(topStart = 8.dp)
0 to days -> RoundedCornerShape(topEnd = 8.dp)
timeSlots to days -> RoundedCornerShape(bottomEnd = 8.dp)
timeSlots to 0 -> RoundedCornerShape(bottomStart = 8.dp)
else -> RoundedCornerShape(0.dp)
}

Box(
modifier = when (cellType) {
CellType.Blank -> Modifier
.width(42.dp) // 고정 너비
.height(36.dp) // 고정 높이
CellType.TimeHeader -> Modifier
.width(42.dp)
.height(32.dp)

CellType.DateHeader -> Modifier
.weight(1f) // 날짜 셀은 남은 공간 비율로 채움
.height(36.dp)

else -> Modifier
.weight(1f) // 나머지 셀은 남은 공간 비율로 채움
.height(32.dp)
}
.background(
color = backgroundColor,
shape = shape
)
.drawBehind {
val borderWidth = 1.dp.toPx()
val borderColor = Gray200

// 위쪽 선 그리기
if (rowIndex > 0) {
drawLine(
color = borderColor,
start = Offset(0f, 0f),
end = Offset(size.width, 0f),
strokeWidth = borderWidth
)
}

// 왼쪽 선 그리기
if (columnIndex > 0) {
drawLine(
color = borderColor,
start = Offset(0f, 0f),
end = Offset(0f, size.height),
strokeWidth = borderWidth
)
}
},
contentAlignment = Alignment.Center
) {
Text(
text = text,
style = NoostakTheme.typography.c4Regular,
color = NoostakTheme.colors.gray600,
textAlign = TextAlign.Center
)
}
}
}
}
}

fun calculateTimeSlots(startTime: String, endTime: String): Int {
val startHour = startTime.split(":")[0].toInt()
val endHour = endTime.split(":")[0].toInt()
return endHour - startHour
}

fun formatDateHeader(date: String): String {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val parsedDate = LocalDate.parse(date, formatter)

val dayOfWeek = parsedDate.dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.KOREAN)
val month = "%02d".format(parsedDate.monthValue)
val day = "%02d".format(parsedDate.dayOfMonth)

return "$dayOfWeek\n$month/$day"
}

@Preview(showBackground = true)
@Composable
fun NoostakTimeTablePreview() {
fun NoostakTimeTable1Preview() {
NoostakAndroidTheme {
val data = TimeTableEntity(
startTime = "09:00",
Expand All @@ -205,6 +147,11 @@ fun NoostakTimeTablePreview() {
startTime = "15:00",
endTime = "16:00",
level = 60
),
AvailableTimeEntity(
startTime = "17:00",
endTime = "18:00",
level = 80
)
)
),
Expand All @@ -230,7 +177,12 @@ fun NoostakTimeTablePreview() {
)
)
)

NoostakTimeTable(data = data)
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
NoostakTimeTable(data = data)
}
}
}
}
Loading

0 comments on commit 48842ad

Please sign in to comment.