Skip to content

Instantly share code, notes, and snippets.

@halilozercan
Created April 15, 2021 15:06
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save halilozercan/32c3270e1f4dc79449b4d0f28e4e30a6 to your computer and use it in GitHub Desktop.
Save halilozercan/32c3270e1f4dc79449b4d0f28e4e30a6 to your computer and use it in GitHub Desktop.
@Composable
fun CalendarView(
modifier: Modifier = Modifier,
selectedDay: CalendarDay = CalendarDay.create(),
onSelectedDayChange: (CalendarDay) -> Unit = {},
visibleMonth: CalendarDay = CalendarDay.create(),
onVisibleMonthChange: (CalendarDay) -> Unit = {},
today: CalendarDay = CalendarDay.create(),
events: Set<CalendarDay> = emptySet()
) {
val currentMonthCalendar = remember(visibleMonth) { visibleMonth.toCalendar() }
Column(modifier = modifier.background(N0)) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 24.dp)) {
Text(
SimpleDateFormat("MMMM YYYY").format(currentMonthCalendar.time),
fontSize = 16.sp,
fontWeight = FontWeight.W500,
color = N800,
lineHeight = 20.sp
)
Spacer(Modifier.weight(1f))
IconButton(onClick = {
onVisibleMonthChange(visibleMonth.monthBefore)
}) {
Icon(
svgResource("arrow_down.svg"),
contentDescription = "Previous month",
tint = B400,
modifier = Modifier.rotate(90f)
)
}
IconButton(onClick = {
onVisibleMonthChange(visibleMonth.monthAfter)
}) {
Icon(
svgResource("arrow_down.svg"),
contentDescription = "Next month",
tint = B400,
modifier = Modifier.rotate(270f)
)
}
}
SlideInOutLayout(visibleMonth.year * 12 + visibleMonth.month) { index ->
CalendarPage(
month = CalendarDay(day = 1, month = index % 12, year = index / 12),
onDayClick = {
onVisibleMonthChange(CalendarDay(day = 1, month = it.month, year = it.year))
onSelectedDayChange(it)
},
selectedDay = selectedDay,
today = today,
events = events
)
}
}
}
@Composable
fun CalendarPage(
month: CalendarDay,
onDayClick: (CalendarDay) -> Unit,
selectedDay: CalendarDay,
today: CalendarDay,
events: Set<CalendarDay>
) {
Column(modifier = Modifier.fillMaxWidth()) {
val startDayCalendar = month.toCalendar().firstFirstDayOfWeekBeforeThisMonth()
val iteratorCalendar = startDayCalendar.clone() as Calendar
Row(modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp)) {
repeat(7) {
Text(
text = (SimpleDateFormat("EEE").format(iteratorCalendar.time).toUpperCase()),
fontSize = 12.sp,
color = N100,
lineHeight = 16.sp,
fontFamily = sfpTextFontFamily,
textAlign = TextAlign.Center,
modifier = Modifier.weight(1f)
)
iteratorCalendar.add(Calendar.DATE, 1)
}
iteratorCalendar.add(Calendar.DATE, -7)
}
repeat(6) { // 6 weeks
Row(modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp)) {
repeat(7) { // 7 days a week
val calendarDay = iteratorCalendar.toCalendarDay()
CalendarDayView(
number = calendarDay.day,
onClick = { onDayClick(calendarDay) },
isSelected = calendarDay == selectedDay,
isToday = calendarDay == today,
isEventful = events.contains(calendarDay),
isInCurrentMonth = month.month == calendarDay.month && month.year == calendarDay.year
)
iteratorCalendar.add(Calendar.DATE, 1)
}
}
}
}
}
@Composable
fun RowScope.CalendarDayView(
number: Int,
onClick: () -> Unit,
isSelected: Boolean,
isToday: Boolean,
isEventful: Boolean,
isInCurrentMonth: Boolean,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
.weight(1f)
.padding(horizontal = 2.dp)
.alpha(if (isInCurrentMonth) 1f else 0.4f)
.clip(RoundedCornerShape(8.dp))
.background(if (isSelected) B400 else N0)
.clickable { onClick() }
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
"$number",
fontSize = 14.sp,
lineHeight = 16.71.sp,
color = if (!isSelected) N800 else N0,
fontWeight = if (isToday) FontWeight.W700 else FontWeight.Normal
)
Spacer(Modifier.height(6.dp))
Canvas(Modifier.size(6.dp).alpha(if (isEventful) 1f else 0f)) {
drawCircle(if (isSelected) N0 else B400)
}
}
}
fun Calendar.firstFirstDayOfWeekBeforeThisMonth(): Calendar {
return (this.clone() as Calendar).apply {
set(Calendar.DAY_OF_MONTH, 1)
while (get(Calendar.DAY_OF_WEEK) != firstDayOfWeek) {
add(Calendar.DATE, -1)
}
}
}
data class CalendarDay(
val day: Int,
val month: Int,
val year: Int
) {
companion object {
fun create(): CalendarDay = with(Calendar.getInstance()) {
CalendarDay(
day = get(Calendar.DAY_OF_MONTH),
month = get(Calendar.MONTH),
year = get(Calendar.YEAR)
)
}
}
}
val CalendarDay.monthBefore: CalendarDay
get() = toCalendar().apply { add(Calendar.MONTH, -1) }.toCalendarDay()
val CalendarDay.monthAfter: CalendarDay
get() = toCalendar().apply { add(Calendar.MONTH, 1) }.toCalendarDay()
fun Calendar.toCalendarDay(): CalendarDay = CalendarDay(
day = get(Calendar.DAY_OF_MONTH),
month = get(Calendar.MONTH),
year = get(Calendar.YEAR),
)
fun CalendarDay.toCalendar(): Calendar = Calendar.getInstance().apply {
set(year, month, day)
firstDayOfWeek = Calendar.SUNDAY
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment