Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UI/#20] 캘린더뷰 / 주간 캘린더 구현 #33

Merged
merged 16 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.terning.core.designsystem.component.box

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
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.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.terning.core.designsystem.theme.CalPink
import com.terning.core.designsystem.theme.CalPurple
import com.terning.core.designsystem.theme.Grey150
import com.terning.core.designsystem.theme.White

/**
* ScrapBox is made for easy customization of scrap box used in Calendar & Home Screen
*
* [modifier] must be assigned for assigning size of the box and padding
* [elevation] must be set greater than zero for shadow effect, mainly used in Calendar
* [borderWidth] must be set greater than zero for border effect, mainly used in Home
*/

@Composable
fun ScrapBox(
cornerRadius: Dp,
scrapColor: Color,
modifier: Modifier = Modifier,
elevation: Dp = 0.dp,
borderWidth: Dp = 0.dp,
borderColor: Color = Grey150,
content: @Composable () -> Unit,
) {
Box(
modifier = modifier
.border(
width = borderWidth,
color = borderColor,
RoundedCornerShape(cornerRadius),
)
.shadow(
elevation = elevation,
RoundedCornerShape(cornerRadius),
)
) {
Box(
modifier = Modifier
.background(
color = scrapColor,
shape = RoundedCornerShape(cornerRadius)
)
.fillMaxSize()
)
Box(
modifier = Modifier
.fillMaxSize()
.padding(start = 9.dp)
.background(
shape = RoundedCornerShape(
topEnd = cornerRadius, bottomEnd = cornerRadius
), color = White
)
) {
content()
}
}
}

@Preview(showBackground = true)
@Composable
fun BorderedScrapBoxPreview() {
ScrapBox(
modifier = Modifier
.height(116.dp)
.width(140.dp),
scrapColor = CalPink,
cornerRadius = 5.dp,
borderWidth = 1.dp
) {}
}

@Preview(showBackground = true)
@Composable
fun ShadowedScrapBoxPreview() {
ScrapBox(
modifier = Modifier
.height(height = 92.dp)
.fillMaxWidth(),
scrapColor = CalPurple,
cornerRadius = 10.dp,
elevation = 1.dp
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ package com.terning.core.extension
import java.time.LocalDate

fun LocalDate.getStringAsTitle(): String =
"${year}년 ${monthValue.toString().padStart(2, '0')}월"
"${year}년 ${monthValue.toString().padStart(2, '0')}월"

fun LocalDate.getWeekIndexContainingSelectedDate(): Int = dayOfMonth / 7

fun LocalDate.isToday(): Boolean = this == LocalDate.now()
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.terning.core.designsystem.theme.Grey150
import com.terning.core.designsystem.theme.TerningPointTheme
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.extension.isToday
import com.terning.feature.calendar.models.MonthData
import com.terning.feature.calendar.models.Scrap
import com.terning.feature.calendar.models.SelectedDateState
import java.time.LocalDate
import java.time.YearMonth

Expand All @@ -26,7 +27,7 @@ fun CalendarMonth(
modifier: Modifier = Modifier,
monthData: MonthData,
onDateSelected: (LocalDate) -> Unit,
selectedDate: LocalDate,
selectedDate: SelectedDateState,
scrapLists: List<List<Scrap>> = listOf()
) {
Column(
Expand All @@ -48,8 +49,8 @@ fun CalendarMonth(
) {
CalendarDay(
dayData = day,
isSelected = selectedDate == day.date,
isToday = day.date == LocalDate.now(),
isSelected = selectedDate.selectedDate == day.date && selectedDate.isEnabled,
isToday = day.date.isToday(),
onDateSelected = onDateSelected
)
if(!day.isOutDate) {
Expand Down Expand Up @@ -79,7 +80,7 @@ fun CalendarMonthPreview() {
TerningPointTheme {
CalendarMonth(
monthData = MonthData(YearMonth.now()),
selectedDate = LocalDate.now(),
selectedDate = SelectedDateState(LocalDate.now(), true),
onDateSelected = {},
scrapLists = listOf()
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.terning.feature.calendar

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.runtime.Composable
Expand All @@ -11,17 +10,18 @@ import com.terning.feature.calendar.models.CalendarDefaults.flingBehavior
import com.terning.feature.calendar.models.CalendarState.Companion.getDateByPage
import com.terning.feature.calendar.models.MonthData
import com.terning.feature.calendar.models.Scrap
import com.terning.feature.calendar.models.SelectedDateState
import java.time.LocalDate
import java.time.YearMonth

@Composable
fun CalendarMonths(
modifier: Modifier = Modifier,
listState: LazyListState,
onDateSelected: (LocalDate) -> Unit,
pages: Int,
selectedDate: LocalDate,
scrapLists: List<List<Scrap>>
selectedDate: SelectedDateState,
scrapLists: List<List<Scrap>>,
modifier: Modifier = Modifier,
) {
LazyRow(
modifier = modifier
Expand Down
92 changes: 65 additions & 27 deletions feature/src/main/java/com/terning/feature/calendar/CalendarRoute.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.terning.feature.calendar

import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
Expand All @@ -21,14 +26,18 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.terning.core.designsystem.theme.Grey200
import com.terning.feature.R
import com.terning.feature.calendar.component.CalendarTopBar
import com.terning.feature.calendar.component.WeekDaysHeader
import com.terning.feature.calendar.models.CalendarState
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import java.time.LocalDate

@Composable
fun CalendarRoute() {
Expand All @@ -40,35 +49,33 @@ fun CalendarScreen(
modifier: Modifier = Modifier,
viewModel: CalendarViewModel = hiltViewModel()
) {
val selectedDate by viewModel.selectedDate.collectAsState()
val state by remember{ mutableStateOf(CalendarState()) }
val selectedDate by viewModel.selectedDate.collectAsStateWithLifecycle()
val state by remember { mutableStateOf(CalendarState()) }

val listState = rememberLazyListState(
initialFirstVisibleItemIndex = state.getInitialPage()
)

var currentDate by remember { mutableStateOf(selectedDate) }
var currentPage by remember { mutableIntStateOf(listState.firstVisibleItemIndex)}
var currentDate by remember { mutableStateOf(LocalDate.now()) }
var currentPage by remember { mutableIntStateOf(listState.firstVisibleItemIndex) }
Comment on lines +59 to +60
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 rememberSaveable로 해야 되나 했는데 저희 서비스는 가로모드 대응 안 하기 때문에 안 바꿔도 될 것 같네용


var isListExpanded by remember { mutableStateOf(false) }
var isWeekEnabled by remember { mutableStateOf(false) }

LaunchedEffect(key1 = listState) {
snapshotFlow { listState.firstVisibleItemIndex }
.distinctUntilChanged()
.collect{
val swipeDirection = (listState.firstVisibleItemIndex - currentPage).coerceIn(-1, 1).toLong()
.collect {
val swipeDirection =
(listState.firstVisibleItemIndex - currentPage).coerceIn(-1, 1).toLong()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오!! coerceIn() 처음 보는 것 같은데!! 새로운거 알아갑니당ㅎㅎ

currentDate = currentDate.plusMonths(swipeDirection)
currentPage = listState.firstVisibleItemIndex
}
}

LaunchedEffect(key1 = selectedDate) {
isWeekEnabled = true
}

BackHandler {
isWeekEnabled = false
if (selectedDate.isEnabled) {
viewModel.updateSelectedDate(selectedDate.selectedDate)
}
}

Scaffold(
Expand All @@ -91,26 +98,57 @@ fun CalendarScreen(
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxSize()
.padding(top = paddingValues.calculateTopPadding())
) {
WeekDaysHeader()
Spacer(modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(color = Grey200)
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(color = Grey200)
)
CalendarMonths(
modifier = Modifier.fillMaxSize(),
selectedDate = selectedDate,
onDateSelected = {
viewModel.updateSelectedDate(it)

AnimatedContent(
targetState = selectedDate.isEnabled,
transitionSpec = {
if (!targetState) {
slideInVertically { fullHeight -> -fullHeight } togetherWith
slideOutVertically { fullHeight -> fullHeight }
} else {
slideInVertically { fullHeight -> fullHeight } togetherWith
slideOutVertically { fullHeight -> -fullHeight }
}.using(
sizeTransform = SizeTransform(clip = true)
)
},
listState = listState,
pages = state.getPageCount(),
scrapLists = viewModel.mockScrapList
)
label = stringResource(id = R.string.calendar_animation_label)
) { targetState ->
if (!targetState) {
CalendarMonths(
modifier = Modifier.fillMaxSize(),
selectedDate = selectedDate,
onDateSelected = {
viewModel.updateSelectedDate(it)
},
listState = listState,
pages = state.getPageCount(),
scrapLists = viewModel.mockScrapList,
)
} else {
CalendarWeekWithScrap(
modifier = Modifier
.fillMaxSize(),
selectedDate = selectedDate,
scrapLists = viewModel.mockScrapList,
onDateSelected = {
viewModel.updateSelectedDate(it)
}
)
}
}
}

}
}


Loading
Loading