Skip to content

Commit

Permalink
Fix lint
Browse files Browse the repository at this point in the history
  • Loading branch information
WojciechOsak committed Mar 14, 2024
1 parent 8db43ec commit 61132b9
Show file tree
Hide file tree
Showing 21 changed files with 338 additions and 115 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
root = true

[*.{kt,kts}]
ktlint_function_naming_ignore_when_annotated_with=Composable
2 changes: 1 addition & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Features:
| Single selection ||
| Multiple selection ||
| Month/Year picker | 🔜 |
| Scroll to date animation | 🔜 |
| Scroll to date animation | |
| Vertical calendar ||
| Range selection | 🔜 |

Expand Down
1 change: 1 addition & 0 deletions calendar/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ kotlin {
}
}


android {
namespace = "com.wojciechosak.calendar"
compileSdk = 34
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.wojciechosak.calendar.animation

import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.pager.PagerState
import io.wojciechosak.calendar.config.CalendarConstants.INITIAL_PAGE_INDEX
import io.wojciechosak.calendar.utils.copy
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.LocalDate
import kotlinx.datetime.daysUntil
import kotlinx.datetime.periodUntil
import kotlinx.datetime.plus
import kotlin.math.floor

@OptIn(ExperimentalFoundationApi::class)
class CalendarAnimator(private val startDate: LocalDate) {
enum class AnimationMode {
MONTH,
DAY,
WEEK,
}

private var pagerState: PagerState? = null

private var mode: AnimationMode = AnimationMode.MONTH

fun updatePagerState(pagerState: PagerState) {
this.pagerState = pagerState
}

internal fun setAnimationMode(mode: AnimationMode) {
this.mode = mode
}

suspend fun animateTo(
target: LocalDate,
pageOffsetFraction: Float = 0f,
animationSpec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow),
) {
val initialPage = pagerState?.initialPage ?: INITIAL_PAGE_INDEX
val currentDate =
when (mode) {
AnimationMode.MONTH ->
startDate.plus(
(pagerState?.targetPage ?: 0) - initialPage,
DateTimeUnit.MONTH,
)

AnimationMode.DAY ->
startDate.plus(
(pagerState?.targetPage ?: 0) - initialPage,
DateTimeUnit.DAY,
)

AnimationMode.WEEK ->
startDate.plus(
(pagerState?.targetPage ?: 0) - initialPage,
DateTimeUnit.WEEK,
)
}
val targetDate =
target.run {
if (mode == AnimationMode.MONTH) {
copy(day = currentDate.dayOfMonth)
} else {
this
}
}
val diff = currentDate.periodUntil(targetDate)
val offset =
when (mode) {
AnimationMode.MONTH -> diff.months + diff.years * 12
AnimationMode.DAY -> currentDate.daysUntil(targetDate)
AnimationMode.WEEK -> floor(currentDate.daysUntil(targetDate) / 7f).toInt()
}
if (initialPage + offset > 0) {
pagerState?.animateScrollToPage(
page = (pagerState?.settledPage ?: initialPage) + offset,
pageOffsetFraction = pageOffsetFraction,
animationSpec = animationSpec,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import io.wojciechosak.calendar.utils.asYearMonth
import io.wojciechosak.calendar.utils.today
import io.wojciechosak.calendar.utils.toMonthYear
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.LocalDate
import kotlinx.datetime.plus
Expand All @@ -15,7 +14,7 @@ import kotlinx.datetime.plus
class CalendarConfig(
val minDate: LocalDate,
val maxDate: LocalDate,
val yearMonth: YearMonth,
val monthYear: MonthYear,
val dayOfWeekOffset: Int,
val showNextMonthDays: Boolean,
val showPreviousMonthDays: Boolean,
Expand All @@ -25,9 +24,10 @@ class CalendarConfig(

@Composable
fun rememberCalendarState(
startDate: LocalDate,
minDate: LocalDate = LocalDate(1971, 1, 1),
maxDate: LocalDate = LocalDate.today().plus(15, DateTimeUnit.YEAR),
yearMonth: YearMonth = LocalDate.today().asYearMonth(),
maxDate: LocalDate = startDate.plus(15, DateTimeUnit.YEAR),
monthOffset: Int,
dayOfWeekOffset: Int = 0,
showNextMonthDays: Boolean = true,
showPreviousMonthDays: Boolean = true,
Expand All @@ -39,7 +39,7 @@ fun rememberCalendarState(
CalendarConfig(
minDate = minDate,
maxDate = maxDate,
yearMonth = yearMonth,
monthYear = startDate.plus(monthOffset, DateTimeUnit.MONTH).toMonthYear(),
dayOfWeekOffset = dayOfWeekOffset,
showNextMonthDays = showNextMonthDays,
showPreviousMonthDays = showPreviousMonthDays,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.wojciechosak.calendar.config

object CalendarConstants {
internal const val INITIAL_PAGE_INDEX = Int.MAX_VALUE / 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.wojciechosak.calendar.config

import io.wojciechosak.calendar.utils.toMonthYear
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.LocalDate
import kotlinx.datetime.Month
import kotlinx.datetime.plus

data class MonthYear(val month: Month, val year: Int)

fun MonthYear.toLocalDate() = LocalDate(year, month, 1)

fun MonthYear.monthOffset(monthOffset: Int) =
this
.toLocalDate()
.plus(monthOffset, DateTimeUnit.MONTH)
.toMonthYear()

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.wojciechosak.calendar.utils

import io.wojciechosak.calendar.config.YearMonth
import io.wojciechosak.calendar.config.MonthYear
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
Expand Down Expand Up @@ -47,6 +47,6 @@ fun LocalDate.Companion.today(): LocalDate {
.toLocalDate()
}

fun LocalDate.asYearMonth(): YearMonth {
return YearMonth(this.year, this.month)
fun LocalDate.toMonthYear(): MonthYear {
return MonthYear(this.month, this.year)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.sp
import io.wojciechosak.calendar.config.CalendarConfig
import io.wojciechosak.calendar.config.DayState
import io.wojciechosak.calendar.config.YearMonth
import io.wojciechosak.calendar.config.rememberCalendarState
import io.wojciechosak.calendar.config.MonthYear
import io.wojciechosak.calendar.utils.monthLength
import io.wojciechosak.calendar.utils.today
import kotlinx.datetime.DateTimeUnit
Expand All @@ -27,7 +25,7 @@ import kotlinx.datetime.plus

@Composable
fun CalendarView(
config: State<CalendarConfig> = rememberCalendarState(),
config: State<CalendarConfig>,
horizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceEvenly,
verticalArrangement: Arrangement.Vertical = Arrangement.SpaceEvenly,
isActiveDay: (LocalDate) -> Boolean = { LocalDate.today() == it },
Expand All @@ -53,7 +51,8 @@ fun CalendarView(
},
modifier: Modifier = Modifier,
) {
val yearMonth by remember { mutableStateOf(config.value.yearMonth) }
val yearMonth by remember { mutableStateOf(config.value.monthYear) }

val daysInCurrentMonth by remember {
mutableStateOf(
monthLength(
Expand Down Expand Up @@ -140,13 +139,13 @@ fun CalendarView(
}
}

private fun calculateVisibleDaysOfPreviousMonth(yearMonth: YearMonth): Int {
val (year, month) = yearMonth
private fun calculateVisibleDaysOfPreviousMonth(monthYear: MonthYear): Int {
val (month, year) = monthYear
return LocalDate(year = year, month = month, dayOfMonth = 1).dayOfWeek.ordinal
}

private fun calculateVisibleDaysOfNextMonth(yearMonth: YearMonth): Int {
val (year, month) = yearMonth
private fun calculateVisibleDaysOfNextMonth(monthYear: MonthYear): Int {
val (month, year) = monthYear
val daysInMonth = monthLength(month, year)
val lastMonthDay = LocalDate(year = year, month = month, dayOfMonth = daysInMonth)
return 6 - lastMonthDay.dayOfWeek.ordinal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,53 @@ package io.wojciechosak.calendar.view
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PageSize
import androidx.compose.foundation.pager.PagerDefaults
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.unit.dp
import io.wojciechosak.calendar.animation.CalendarAnimator
import io.wojciechosak.calendar.config.CalendarConstants.INITIAL_PAGE_INDEX
import kotlinx.datetime.LocalDate

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun HorizontalCalendarView(
startDate: LocalDate,
modifier: Modifier = Modifier,
pageSize: PageSize = PageSize.Fill,
beyondBoundsPageCount: Int = 0,
contentPadding: PaddingValues = PaddingValues(0.dp),
pageNestedScrollConnection: NestedScrollConnection =
PagerDefaults.pageNestedScrollConnection(
Orientation.Horizontal,
),
calendarAnimator: CalendarAnimator = CalendarAnimator(startDate),
calendarView: @Composable (monthOffset: Int) -> Unit,
) {
val pagerState =
rememberPagerState(
initialPage = INITIAL_PAGE_INDEX,
pageCount = { Int.MAX_VALUE },
)
HorizontalPager(
state =
rememberPagerState(
initialPage = Int.MAX_VALUE / 2,
pageCount = { Int.MAX_VALUE },
),
state = pagerState,
modifier = modifier,
pageSize = pageSize,
beyondBoundsPageCount = beyondBoundsPageCount,
pageNestedScrollConnection = pageNestedScrollConnection,
contentPadding = contentPadding,
) {
val index = it - Int.MAX_VALUE / 2
val index = it - INITIAL_PAGE_INDEX
calendarAnimator.updatePagerState(pagerState)
LaunchedEffect(Unit) {
calendarAnimator.setAnimationMode(CalendarAnimator.AnimationMode.MONTH)
}
Column {
calendarView(index)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,46 @@
package io.wojciechosak.calendar.view

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.pager.PageSize
import androidx.compose.foundation.pager.VerticalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.wojciechosak.calendar.animation.CalendarAnimator
import io.wojciechosak.calendar.config.CalendarConstants.INITIAL_PAGE_INDEX
import kotlinx.datetime.LocalDate

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun VerticalCalendarView(
startDate: LocalDate,
calendarAnimator: CalendarAnimator = CalendarAnimator(startDate),
modifier: Modifier = Modifier,
pageSize: PageSize = PageSize.Fill,
contentPadding: PaddingValues = PaddingValues(0.dp),
calendarView: @Composable (monthOffset: Int) -> Unit,
) {
val pagerState =
rememberPagerState(
initialPage = INITIAL_PAGE_INDEX,
pageCount = { Int.MAX_VALUE },
)
LaunchedEffect(pagerState) {
calendarAnimator.setAnimationMode(CalendarAnimator.AnimationMode.MONTH)
calendarAnimator.updatePagerState(pagerState)
}
VerticalPager(
state =
rememberPagerState(
initialPage = Int.MAX_VALUE / 2,
pageCount = { Int.MAX_VALUE },
),
state = pagerState,
modifier = modifier.fillMaxWidth(),
pageSize = pageSize,
beyondBoundsPageCount = 0,
contentPadding = contentPadding,
) {
val index = it - Int.MAX_VALUE / 2
Column {
calendarView(index)
}
val index = it - INITIAL_PAGE_INDEX
calendarView(index)
}
}
Loading

0 comments on commit 61132b9

Please sign in to comment.