Skip to content

Commit

Permalink
Added support for min and max month (#113)
Browse files Browse the repository at this point in the history
* Added support for min and max month

* Fixed test results

* Fixed detekt issues

* Forgot apiDump
  • Loading branch information
kevinvanmierlo authored Jan 23, 2024
1 parent 3ad64ca commit a3ad7a4
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 46 deletions.
10 changes: 7 additions & 3 deletions library/api/library.api
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ public final class io/github/boguszpawlowski/composecalendar/CalendarKt {
public static final fun Calendar (Lio/github/boguszpawlowski/composecalendar/CalendarState;Landroidx/compose/ui/Modifier;Ljava/time/DayOfWeek;Ljava/time/LocalDate;ZZZLkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V
public static final fun SelectableCalendar (Landroidx/compose/ui/Modifier;Ljava/time/DayOfWeek;Ljava/time/LocalDate;ZZZLio/github/boguszpawlowski/composecalendar/CalendarState;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V
public static final fun StaticCalendar (Landroidx/compose/ui/Modifier;Ljava/time/DayOfWeek;Ljava/time/LocalDate;ZZZLio/github/boguszpawlowski/composecalendar/CalendarState;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V
public static final fun rememberCalendarState (Ljava/time/YearMonth;Lio/github/boguszpawlowski/composecalendar/header/MonthState;Landroidx/compose/runtime/Composer;II)Lio/github/boguszpawlowski/composecalendar/CalendarState;
public static final fun rememberSelectableCalendarState (Ljava/time/YearMonth;Ljava/util/List;Lio/github/boguszpawlowski/composecalendar/selection/SelectionMode;Lkotlin/jvm/functions/Function1;Lio/github/boguszpawlowski/composecalendar/header/MonthState;Lio/github/boguszpawlowski/composecalendar/selection/DynamicSelectionState;Landroidx/compose/runtime/Composer;II)Lio/github/boguszpawlowski/composecalendar/CalendarState;
public static final fun rememberCalendarState (Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/YearMonth;Lio/github/boguszpawlowski/composecalendar/header/MonthState;Landroidx/compose/runtime/Composer;II)Lio/github/boguszpawlowski/composecalendar/CalendarState;
public static final fun rememberSelectableCalendarState (Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/util/List;Lio/github/boguszpawlowski/composecalendar/selection/SelectionMode;Lkotlin/jvm/functions/Function1;Lio/github/boguszpawlowski/composecalendar/header/MonthState;Lio/github/boguszpawlowski/composecalendar/selection/DynamicSelectionState;Landroidx/compose/runtime/Composer;II)Lio/github/boguszpawlowski/composecalendar/CalendarState;
}

public final class io/github/boguszpawlowski/composecalendar/CalendarState {
Expand Down Expand Up @@ -141,15 +141,19 @@ public final class io/github/boguszpawlowski/composecalendar/header/DefaultWeekH
public abstract interface class io/github/boguszpawlowski/composecalendar/header/MonthState {
public static final field Companion Lio/github/boguszpawlowski/composecalendar/header/MonthState$Companion;
public abstract fun getCurrentMonth ()Ljava/time/YearMonth;
public abstract fun getMaxMonth ()Ljava/time/YearMonth;
public abstract fun getMinMonth ()Ljava/time/YearMonth;
public abstract fun setCurrentMonth (Ljava/time/YearMonth;)V
public abstract fun setMaxMonth (Ljava/time/YearMonth;)V
public abstract fun setMinMonth (Ljava/time/YearMonth;)V
}

public final class io/github/boguszpawlowski/composecalendar/header/MonthState$Companion {
public final fun Saver ()Landroidx/compose/runtime/saveable/Saver;
}

public final class io/github/boguszpawlowski/composecalendar/header/MonthStateKt {
public static final fun MonthState (Ljava/time/YearMonth;)Lio/github/boguszpawlowski/composecalendar/header/MonthState;
public static final fun MonthState (Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/YearMonth;)Lio/github/boguszpawlowski/composecalendar/header/MonthState;
}

public abstract interface class io/github/boguszpawlowski/composecalendar/header/WeekState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,17 @@ public fun <T : SelectionState> Calendar(
@Composable
public fun rememberSelectableCalendarState(
initialMonth: YearMonth = YearMonth.now(),
minMonth: YearMonth = initialMonth.minusMonths(DefaultCalendarMonthRange),
maxMonth: YearMonth = initialMonth.plusMonths(DefaultCalendarMonthRange),
initialSelection: List<LocalDate> = emptyList(),
initialSelectionMode: SelectionMode = SelectionMode.Single,
confirmSelectionChange: (newValue: List<LocalDate>) -> Boolean = { true },
monthState: MonthState = rememberSaveable(saver = MonthState.Saver()) {
MonthState(initialMonth = initialMonth)
MonthState(
initialMonth = initialMonth,
minMonth = minMonth,
maxMonth = maxMonth
)
},
selectionState: DynamicSelectionState = rememberSaveable(
saver = DynamicSelectionState.Saver(confirmSelectionChange),
Expand All @@ -256,7 +262,15 @@ public fun rememberSelectableCalendarState(
@Composable
public fun rememberCalendarState(
initialMonth: YearMonth = YearMonth.now(),
minMonth: YearMonth = initialMonth.minusMonths(DefaultCalendarMonthRange),
maxMonth: YearMonth = initialMonth.plusMonths(DefaultCalendarMonthRange),
monthState: MonthState = rememberSaveable(saver = MonthState.Saver()) {
MonthState(initialMonth = initialMonth)
MonthState(
initialMonth = initialMonth,
minMonth = minMonth,
maxMonth = maxMonth
)
},
): CalendarState<EmptySelectionState> = remember { CalendarState(monthState, EmptySelectionState) }

internal const val DefaultCalendarMonthRange = 10000L
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,7 @@ public fun DefaultMonthHeader(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(
modifier = Modifier.testTag("Decrement"),
onClick = { monthState.currentMonth = monthState.currentMonth.minusMonths(1) }
) {
Image(
imageVector = Icons.Default.KeyboardArrowLeft,
colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface),
contentDescription = "Previous",
)
}
DecrementButton(monthState = monthState)
Text(
modifier = Modifier.testTag("MonthLabel"),
text = monthState.currentMonth.month
Expand All @@ -55,15 +46,40 @@ public fun DefaultMonthHeader(
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = monthState.currentMonth.year.toString(), style = MaterialTheme.typography.h4)
IconButton(
modifier = Modifier.testTag("Increment"),
onClick = { monthState.currentMonth = monthState.currentMonth.plusMonths(1) }
) {
Image(
imageVector = Icons.Default.KeyboardArrowRight,
colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface),
contentDescription = "Next",
)
}
IncrementButton(monthState = monthState)
}
}

@Composable
private fun DecrementButton(
monthState: MonthState,
) {
IconButton(
modifier = Modifier.testTag("Decrement"),
enabled = monthState.currentMonth > monthState.minMonth,
onClick = { monthState.currentMonth = monthState.currentMonth.minusMonths(1) }
) {
Image(
imageVector = Icons.Default.KeyboardArrowLeft,
colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface),
contentDescription = "Previous",
)
}
}

@Composable
private fun IncrementButton(
monthState: MonthState,
) {
IconButton(
modifier = Modifier.testTag("Increment"),
enabled = monthState.currentMonth < monthState.maxMonth,
onClick = { monthState.currentMonth = monthState.currentMonth.plusMonths(1) }
) {
Image(
imageVector = Icons.Default.KeyboardArrowRight,
colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface),
contentDescription = "Next",
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,69 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.mapSaver
import androidx.compose.runtime.setValue
import java.time.YearMonth

@Suppress("FunctionName") // Factory function
public fun MonthState(initialMonth: YearMonth): MonthState = MonthStateImpl(initialMonth)
public fun MonthState(
initialMonth: YearMonth,
minMonth: YearMonth,
maxMonth: YearMonth,
): MonthState = MonthStateImpl(initialMonth, minMonth, maxMonth)

@Stable
public interface MonthState {
public var currentMonth: YearMonth
public var minMonth: YearMonth
public var maxMonth: YearMonth

public companion object {
@Suppress("FunctionName") // Factory function
public fun Saver(): Saver<MonthState, String> = Saver(
save = { it.currentMonth.toString() },
restore = { MonthState(YearMonth.parse(it)) }
public fun Saver(): Saver<MonthState, Any> = mapSaver(
save = { monthState ->
mapOf(
"currentMonth" to monthState.currentMonth.toString(),
"minMonth" to monthState.minMonth.toString(),
"maxMonth" to monthState.maxMonth.toString(),
)
},
restore = { restoreMap ->
MonthState(
YearMonth.parse(restoreMap["currentMonth"] as String),
YearMonth.parse(restoreMap["minMonth"] as String),
YearMonth.parse(restoreMap["maxMonth"] as String),
)
}
)
}
}

@Stable
private class MonthStateImpl(
initialMonth: YearMonth,
minMonth: YearMonth,
maxMonth: YearMonth,
) : MonthState {

private var _currentMonth by mutableStateOf<YearMonth>(initialMonth)
private var _minMonth by mutableStateOf<YearMonth>(minMonth)
private var _maxMonth by mutableStateOf<YearMonth>(maxMonth)

override var currentMonth: YearMonth
get() = _currentMonth
set(value) {
_currentMonth = value
}

override var minMonth: YearMonth
get() = _minMonth
set(value) {
_minMonth = value
}

override var maxMonth: YearMonth
get() = _maxMonth
set(value) {
_maxMonth = value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import io.github.boguszpawlowski.composecalendar.week.getWeeks
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.YearMonth
import java.time.temporal.ChronoUnit

@OptIn(ExperimentalSnapperApi::class)
@Composable
Expand All @@ -48,8 +49,11 @@ internal fun <T : SelectionState> MonthPager(
) {
val coroutineScope = rememberCoroutineScope()

val initialFirstVisibleItemIndex = remember(initialMonth, monthState.minMonth) {
ChronoUnit.MONTHS.between(monthState.minMonth, initialMonth).toInt()
}
val listState = rememberLazyListState(
initialFirstVisibleItemIndex = StartIndex,
initialFirstVisibleItemIndex = initialFirstVisibleItemIndex,
)
val flingBehavior = rememberSnapperFlingBehavior(
lazyListState = listState,
Expand Down Expand Up @@ -77,13 +81,21 @@ internal fun <T : SelectionState> MonthPager(
content = { weekHeader(daysOfWeek) },
)
}
val pagerCount = remember(monthState.minMonth, monthState.maxMonth) {
ChronoUnit.MONTHS.between(monthState.minMonth, monthState.maxMonth).toInt() + 1
}
LazyRow(
modifier = modifier.testTag("MonthPager"),
state = listState,
flingBehavior = flingBehavior,
verticalAlignment = Alignment.Top,
) {
items(PagerItemCount) { index ->
items(
count = pagerCount,
key = { index ->
monthListState.getMonthForPage(index).let { "${it.year}-${it.monthValue}" }
}
) { index ->
MonthContent(
modifier = Modifier.fillParentMaxWidth(),
showAdjacentMonths = showAdjacentMonths,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,12 @@ internal class MonthListState(
}

fun getMonthForPage(index: Int): YearMonth =
initialMonth.plusMonths((index - StartIndex).toLong())
monthState.minMonth.plusMonths(index.toLong())

private fun moveToMonth(month: YearMonth) {
if (month == currentFirstVisibleMonth) return
initialMonth.minus(month).let { offset ->
coroutineScope.launch {
listState.animateScrollToItem((StartIndex - offset).toInt())
}
coroutineScope.launch {
listState.animateScrollToItem(ChronoUnit.MONTHS.between(monthState.minMonth, month).toInt())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,15 @@ private class MonthSelectionState(
@Composable
private fun rememberMonthSelectionState(
initialMonth: YearMonth = YearMonth.now(),
minMonth: YearMonth = initialMonth.minusMonths(10000),
maxMonth: YearMonth = initialMonth.plusMonths(10000),
initialSelection: YearMonth? = null,
monthState: MonthState = rememberSaveable(saver = MonthState.Saver()) {
MonthState(initialMonth = initialMonth)
MonthState(
initialMonth = initialMonth,
minMonth = minMonth,
maxMonth = maxMonth
)
},
selectionState: MonthSelectionState = rememberSaveable(saver = MonthSelectionState.Saver()) {
MonthSelectionState(initialSelection = initialSelection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ fun MainScreen() {
composable("custom_selection") { CustomSelectionSample() }
composable("viewmodel") { ViewModelSample() }
composable("kotlinx_datetime") { KotlinXDateTimeSample() }
composable("min_max_month") { MinMaxCalendarSample() }
}
}
}
Expand Down Expand Up @@ -103,5 +104,10 @@ fun MainMenu(navController: NavController) {
Button(onClick = { navController.navigate("kotlinx_datetime") }) {
Text(text = "Kotlinx DateTime")
}
Spacer(modifier = Modifier.height(16.dp))

Button(onClick = { navController.navigate("min_max_month") }) {
Text(text = "Min Max Month")
}
}
}
Loading

0 comments on commit a3ad7a4

Please sign in to comment.