Skip to content

Commit

Permalink
[MERGE] #20 -> develop
Browse files Browse the repository at this point in the history
[UI/#20] 캘린더뷰 / 주간 캘린더 구현
  • Loading branch information
boiledEgg-s authored Jul 10, 2024
2 parents a42f59d + 067947a commit 78b74fb
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 40 deletions.
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) }

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()
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

0 comments on commit 78b74fb

Please sign in to comment.