diff --git a/README.MD b/README.MD index 2780dca..f6be514 100644 --- a/README.MD +++ b/README.MD @@ -46,13 +46,86 @@ Features: | Web(Wasm) | ✅ | | Desktop | ✅ | -# Usage +# Setup +[![Maven Central](https://img.shields.io/badge/dynamic/xml.svg?label=Maven%20Central&color=blue&url=https://repo1.maven.org/maven2/io/github/wojciechosak/calendar/maven-metadata.xml&query=(//metadata/versioning/versions/version)[not(contains(text(),%27-%27))][last()])](https://repo1.maven.org/maven2/io/github/wojciechosak/calendar/) + +In Android project: + +```groovy +dependencies { + implementation 'io.github.wojciechosak:calendar:' +} +``` +In Kotlin Multiplatform project: -# Documentation: +```groovy +commonMain.dependencies { + implementation 'io.github.wojciechosak:calendar:' +} +``` + +```kotlin +CalendarView( + config = rememberCalendarState( + startDate = LocalDate.today(), + monthOffset = 0 + ), + day = { dayState -> + // define your day composable here! + } +) +``` -Documentation can be found here: +# Composables + +Simply use in Compose any view you want: + +| View type | Preview | +|:--------------:|:--------------------------------------------------:| +| CalendarView | | +| HorizontalView | | +| VerticalView | | +| WeekView | | +| MonthPicker | | +| YearPicker | | + +Each view get as parameter day cell composable. Thanks to that your calendar can look whatever you like: + + + +## Range selection: + +```kotlin +CalendarView( + config = rememberCalendarState( + startDate = startDate, + monthOffset = monthOffset, + ), + selectionMode = SelectionMode.Range, + rangeConfig = RangeConfig(rangeIllustrator = RoundedRangeIllustrator(Pallete.LightGreen)), +) +``` + + + +You can draw yourself implementations of RangeIllustrator, or use predefined `RoundedRangeIllustrator` +and `UnderlineIllustrator`. + +## Selection callbacks: + +```kotlin +CalendarView( + config = ..., + onDateSelected = { date -> + // date: List + }, + selectionMode = SelectionMode.Multiply(3) // SelectionMode.Single or SelectionMode.Range +) +``` + +--- # Sample project: diff --git a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/CalendarView.kt b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/CalendarView.kt index 7ba2ea7..3475253 100644 --- a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/CalendarView.kt +++ b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/CalendarView.kt @@ -2,6 +2,7 @@ package io.wojciechosak.calendar.view import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.material3.Text @@ -73,37 +74,38 @@ fun CalendarView( }, ) } + Column { + if (config.value.showHeader) { + header(yearMonth.month, yearMonth.year) + } - if (config.value.showHeader) { - header(yearMonth.month, yearMonth.year) - } - - LazyVerticalGrid( - columns = GridCells.Fixed(7), - horizontalArrangement = horizontalArrangement, - verticalArrangement = verticalArrangement, - userScrollEnabled = false, - modifier = modifier, - ) { - val state = config.value - val weekDaysCount = if (state.showWeekdays) 7 else 0 + LazyVerticalGrid( + columns = GridCells.Fixed(7), + horizontalArrangement = horizontalArrangement, + verticalArrangement = verticalArrangement, + userScrollEnabled = false, + modifier = modifier, + ) { + val state = config.value + val weekDaysCount = if (state.showWeekdays) 7 else 0 - items(previousMonthDays + daysInCurrentMonth + nextMonthDays + weekDaysCount) { iteration -> - Item( - iteration = iteration, - config = config, - weekDaysCount = weekDaysCount, - previousMonthDays = previousMonthDays, - daysInCurrentMonth = daysInCurrentMonth, - dayOfWeekLabel = dayOfWeekLabel, - yearMonth = yearMonth, - state = state, - selectionMode = selectionMode, - onDateSelected = onDateSelected, - isActiveDay = isActiveDay, - rangeConfig = rangeConfig, - day = day, - ) + items(previousMonthDays + daysInCurrentMonth + nextMonthDays + weekDaysCount) { iteration -> + Item( + iteration = iteration, + config = config, + weekDaysCount = weekDaysCount, + previousMonthDays = previousMonthDays, + daysInCurrentMonth = daysInCurrentMonth, + dayOfWeekLabel = dayOfWeekLabel, + yearMonth = yearMonth, + state = state, + selectionMode = selectionMode, + onDateSelected = onDateSelected, + isActiveDay = isActiveDay, + rangeConfig = rangeConfig, + day = day, + ) + } } } } @@ -170,7 +172,7 @@ private fun Item( selectDate( date = newDate, mode = selectionMode, - list = selectedDates, + list = config.value.selectedDates, ) config.value = config.value.copy(selectedDates = selectionList) onDateSelected(selectionList) @@ -202,7 +204,6 @@ private fun selectDate( if (list.firstOrNull() == date) return list val result = mutableListOf() result.addAll(list) - when (mode) { is SelectionMode.Multiply -> { result.add(0, date) diff --git a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/HorizontalCalendarView.kt b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/HorizontalCalendarView.kt index 86d2362..0fce875 100644 --- a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/HorizontalCalendarView.kt +++ b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/HorizontalCalendarView.kt @@ -28,7 +28,7 @@ fun HorizontalCalendarView( ), modifier: Modifier = Modifier, pageSize: PageSize = PageSize.Fill, - beyondBoundsPageCount: Int = 0, + beyondBoundsPageCount: Int = 3, contentPadding: PaddingValues = PaddingValues(0.dp), calendarAnimator: CalendarAnimator = CalendarAnimator(startDate), calendarView: @Composable (monthOffset: Int) -> Unit, diff --git a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/MonthPicker.kt b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/MonthPicker.kt index 99f1a85..6357b9f 100644 --- a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/MonthPicker.kt +++ b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/MonthPicker.kt @@ -2,26 +2,38 @@ package io.wojciechosak.calendar.view import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign import io.wojciechosak.calendar.modifiers.passTouchGesture import kotlinx.datetime.Month @Composable fun MonthPicker( columns: Int = 4, - horizontalArrangement: Arrangement.Horizontal = Arrangement.Center, + horizontalArrangement: Alignment.Horizontal = Alignment.CenterHorizontally, verticalArrangement: Arrangement.Vertical = Arrangement.Center, modifier: Modifier = Modifier, userScrollEnabled: Boolean = true, monthCount: Int = 12, onMonthSelected: (Month) -> Unit = {}, monthView: @Composable (month: Month) -> Unit = { month -> - Text(month.name) + Column( + verticalArrangement = verticalArrangement, + horizontalAlignment = horizontalArrangement, + modifier = Modifier.aspectRatio(1f), + ) { + Text( + month.name, + textAlign = TextAlign.Center, + ) + } }, ) { require(monthCount in 0..12) { @@ -29,8 +41,6 @@ fun MonthPicker( } LazyVerticalGrid( columns = GridCells.Fixed(columns), - horizontalArrangement = horizontalArrangement, - verticalArrangement = verticalArrangement, userScrollEnabled = userScrollEnabled, modifier = modifier, ) { @@ -39,11 +49,7 @@ fun MonthPicker( Box( modifier = Modifier.passTouchGesture { - selectedMonth?.let { month -> - onMonthSelected( - month, - ) - } + selectedMonth?.let { month -> onMonthSelected(month) } }, contentAlignment = Alignment.Center, ) { diff --git a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/VerticalCalendarView.kt b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/VerticalCalendarView.kt index 1d8235f..5fc2d22 100644 --- a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/VerticalCalendarView.kt +++ b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/VerticalCalendarView.kt @@ -23,6 +23,7 @@ fun VerticalCalendarView( modifier: Modifier = Modifier, pageSize: PageSize = PageSize.Fill, contentPadding: PaddingValues = PaddingValues(0.dp), + beyondBoundsPageCount: Int = 3, calendarView: @Composable (monthOffset: Int) -> Unit, ) { val pagerState = @@ -38,7 +39,7 @@ fun VerticalCalendarView( state = pagerState, modifier = modifier.fillMaxWidth(), pageSize = pageSize, - beyondBoundsPageCount = 0, + beyondBoundsPageCount = beyondBoundsPageCount, contentPadding = contentPadding, ) { val index = it - INITIAL_PAGE_INDEX diff --git a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/YearPicker.kt b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/YearPicker.kt index d32fbcb..009e8e9 100644 --- a/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/YearPicker.kt +++ b/calendar/src/commonMain/kotlin/io/wojciechosak/calendar/view/YearPicker.kt @@ -33,7 +33,9 @@ fun YearPicker( pageSize: PageSize = PageSize.Fill, onYearSelected: (Int) -> Unit = {}, yearView: @Composable (year: Int) -> Unit = { year -> - Text(year.toString(), modifier = Modifier.padding(32.dp)) + Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { + Text(year.toString(), modifier = Modifier.padding(35.dp)) + } }, ) { val pagerState = diff --git a/readme/calendar-view.png b/readme/calendar-view.png new file mode 100644 index 0000000..3f9a9fb Binary files /dev/null and b/readme/calendar-view.png differ diff --git a/readme/horizontal.gif b/readme/horizontal.gif new file mode 100644 index 0000000..dc54ad3 Binary files /dev/null and b/readme/horizontal.gif differ diff --git a/readme/monthpicker.png b/readme/monthpicker.png new file mode 100644 index 0000000..1d2f695 Binary files /dev/null and b/readme/monthpicker.png differ diff --git a/readme/range.png b/readme/range.png new file mode 100644 index 0000000..4a9e819 Binary files /dev/null and b/readme/range.png differ diff --git a/readme/sample1.png b/readme/sample1.png new file mode 100644 index 0000000..7e71999 Binary files /dev/null and b/readme/sample1.png differ diff --git a/readme/sample2.png b/readme/sample2.png new file mode 100644 index 0000000..6dff1ca Binary files /dev/null and b/readme/sample2.png differ diff --git a/readme/vertical.gif b/readme/vertical.gif new file mode 100644 index 0000000..f3e7c95 Binary files /dev/null and b/readme/vertical.gif differ diff --git a/readme/weekview.gif b/readme/weekview.gif new file mode 100644 index 0000000..dd44776 Binary files /dev/null and b/readme/weekview.gif differ diff --git a/readme/yearpicker.gif b/readme/yearpicker.gif new file mode 100644 index 0000000..b25424b Binary files /dev/null and b/readme/yearpicker.gif differ diff --git a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/CalendarViewScreen.kt b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/CalendarViewScreen.kt index 21fe1d2..81a2fe8 100644 --- a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/CalendarViewScreen.kt +++ b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/CalendarViewScreen.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeGesturesPadding import androidx.compose.foundation.layout.size import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.OutlinedButton @@ -32,26 +31,24 @@ class CalendarViewScreen : NamedScreen { @Composable override fun Content() { - Box(Modifier.safeGesturesPadding()) { - CalendarView( - day = { state -> - DayView( - date = state.date, - isDotVisible = state.isActiveDay || Random.nextBoolean(), - onClick = { }, - ) - }, - config = - rememberCalendarState( - startDate = MonthYear(year = 1994, month = Month.APRIL).toLocalDate(), - monthOffset = 0, - showNextMonthDays = false, - showPreviousMonthDays = false, - showHeader = false, - showWeekdays = false, - ), - ) - } + CalendarView( + day = { state -> + DayView( + date = state.date, + isDotVisible = state.isActiveDay || Random.nextBoolean(), + onClick = { }, + ) + }, + config = + rememberCalendarState( + startDate = MonthYear(year = 1994, month = Month.APRIL).toLocalDate(), + monthOffset = 0, + showNextMonthDays = false, + showPreviousMonthDays = false, + showHeader = false, + showWeekdays = false, + ), + ) } } diff --git a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/FullDateScreen.kt b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/FullDateScreen.kt index 0ffa8d5..245a2dd 100644 --- a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/FullDateScreen.kt +++ b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/FullDateScreen.kt @@ -2,7 +2,9 @@ package io.wojciechosak.calendar.calendar.screens import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.aspectRatio import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -10,6 +12,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign @@ -49,12 +52,17 @@ class FullDateScreen : NamedScreen { date = date.copy(month = it) mode = DatePickerMode.YEAR }) { - Text( - text = it.name.substring(IntRange(0, 2)).capitalize(), - color = Color.Black, - textAlign = TextAlign.Center, + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.aspectRatio(1f), - ) + ) { + Text( + text = it.name.substring(IntRange(0, 2)).capitalize(), + color = Color.Black, + textAlign = TextAlign.Center, + ) + } } } diff --git a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/MultipleSelectionScreen.kt b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/MultipleSelectionScreen.kt index d9a0708..69607a4 100644 --- a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/MultipleSelectionScreen.kt +++ b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/MultipleSelectionScreen.kt @@ -2,14 +2,7 @@ package io.wojciechosak.calendar.calendar.screens import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp import io.wojciechosak.calendar.config.SelectionMode import io.wojciechosak.calendar.config.rememberCalendarState import io.wojciechosak.calendar.utils.today @@ -26,28 +19,19 @@ class MultipleSelectionScreen : NamedScreen { override fun Content() { Column { val startDate = LocalDate.today() - val selectedDates = remember { mutableStateListOf() } HorizontalCalendarView(startDate = startDate) { monthOffset -> + val config = + rememberCalendarState( + startDate = startDate, + monthOffset = monthOffset, + ) CalendarView( - config = - rememberCalendarState( - startDate = startDate, - monthOffset = monthOffset, - selectedDates = selectedDates, - ), - isActiveDay = { it in selectedDates }, - onDateSelected = { - selectedDates.clear() - selectedDates.addAll(it) - }, + config = config, + isActiveDay = { it in config.value.selectedDates }, selectionMode = SelectionMode.Multiply(3), ) } - Spacer(modifier = Modifier.height(20.dp)) - if (selectedDates.isNotEmpty()) { - Text("Selected:\n${selectedDates.map { "$it\n" }}") - } } } } diff --git a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/RangeSelectionScreen.kt b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/RangeSelectionScreen.kt index 7340e78..815fc73 100644 --- a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/RangeSelectionScreen.kt +++ b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/RangeSelectionScreen.kt @@ -45,9 +45,7 @@ class RangeSelectionScreen : NamedScreen { selectedDates.addAll(it) }, rangeConfig = - RangeConfig( - rangeIllustrator = RoundedRangeIllustrator(Pallete.LightGreen), - ), + RangeConfig(rangeIllustrator = RoundedRangeIllustrator(Pallete.LightGreen)), ) } Spacer(modifier = Modifier.height(20.dp)) diff --git a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/WeekViewScreen.kt b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/WeekViewScreen.kt index 7d43011..21163c2 100644 --- a/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/WeekViewScreen.kt +++ b/sample/composeApp/src/commonMain/kotlin/io/wojciechosak/calendar/calendar/screens/WeekViewScreen.kt @@ -2,7 +2,6 @@ package io.wojciechosak.calendar.calendar.screens import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -12,9 +11,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import io.wojciechosak.calendar.utils.daySimpleName import io.wojciechosak.calendar.utils.today import io.wojciechosak.calendar.view.CalendarDay import io.wojciechosak.calendar.view.WeekView @@ -30,20 +30,16 @@ class WeekViewScreen : NamedScreen { @Composable override fun Content() { Column(modifier = Modifier.padding(10.dp)) { - var monthName by remember { mutableStateOf("") } var selectedDay by remember { mutableStateOf(null) } - Text(monthName, modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center) - - WeekView( - firstVisibleDate = { - monthName = it.month.name - }, - ) { state -> - CalendarDay( - state, - onClick = { }, - ) + WeekView { state -> + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text(state.date.daySimpleName()) + CalendarDay( + state, + onClick = { }, + ) + } } Spacer(modifier = Modifier.height(20.dp))