# Technical Documentation: kizitonwose/Calendar

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/kizitonwose/Calendar)

### Overview (`README.md`)

# Calendar

A highly customizable calendar library for Android and Compose Multiplatform, backed by RecyclerView for the view system, and LazyRow/LazyColumn for compose.

[](https://github.com/kizitonwose/Calendar/actions/workflows/check.yml)
[](https://github.com/kizitonwose/Calendar/blob/main/LICENSE.md)
[](https://twitter.com/kizitonwose)
[[not(contains(text(),%27-%27))][last()])](https://central.sonatype.com/search?q=g:com.kizitonwose.calendar)
[[not(contains(text(),%27-%27))][last()])](https://central.sonatype.com/search?q=g:com.kizitonwose.calendar)

**With this library, your calendar will look however you want it to.**





## Features

- [x] Week, month, or year modes - Show a week-based calendar, or the typical month calendar, or a year-based calendar.
- [x] Single, multiple, or range selection - Total flexibility to implement the date selection
  whichever way you like.
- [x] Disable desired dates - Prevent selection of some dates by disabling them.
- [x] Boundary dates - Limit the calendar date range.
- [x] Custom date view/composable - Make your day cells look however you want, with any
  functionality you want.
- [x] Custom calendar view/composable - Make your calendar look however you want, with whatever
  functionality you want.
- [x] Custom first day of the week - Use any day as the first day of the week.
- [x] Horizontal or vertical scrolling calendar.
- [x] HeatMap calendar - Suitable for showing how data changes over time, like GitHub's contribution
  chart.
- [x] Year/Month/Week headers and footers - Add headers/footers of any kind on each year/month/week.
- [x] Easily scroll to any date/week/month/year on the calendar via swipe actions or programmatically.
- [x] Use all RecyclerView/LazyRow/LazyColumn customizations since the calendar extends from
  RecyclerView for the view system and uses LazyRow/LazyColumn for compose.
- [x] Design your calendar [however you want.](https://github.com/kizitonwose/Calendar/issues/1) The
  library provides the logic, you provide the views/composables.

## Sample project

It's important to check out the sample app. There are lots of examples provided for both view and compose implementations. 
Most techniques that you would want to implement are already done in the examples.

Download the Android sample app [here](https://github.com/kizitonwose/Calendar/releases/download/2.5.4/sample.apk)

View the Android sample app's source code [here](https://github.com/kizitonwose/Calendar/tree/main/sample)

View the multiplatform sample project online at https://calendar.kizitonwose.dev

View the multiplatform sample project's source code [here](https://github.com/kizitonwose/Calendar/tree/main/compose-multiplatform/sample)

## Setup

The library provides the following artifacts: 

`com.kizitonwose.calendar:compose`: The compose artifact for Android projects. This uses the [java.time](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) APIs. 

`com.kizitonwose.calendar:compose-multiplatform`: The compose artifact for Compose Multiplatform projects. This uses the [kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) library and supports Android, iOS, js, WasmJs and Desktop platforms.

`com.kizitonwose.calendar:view`: The view artifact for Android projects. This uses the [java.time](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) APIs and can exist alongside the Android compose artifact in an Android project if needed.

#### Step 1

**This step is required ONLY if your Android app's `minSdkVersion` is below 26. Jump to [step 2](#step-2) if this does not apply to you.**

Android apps with `minSdkVersion` below 26 have to enable [Java 8+ API desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) for backward compatibility since `java.time` classes were added in Java 8 which is supported natively starting from Android SDK 26. To set up your project for desugaring, you need to first ensure that you are using [Android Gradle plugin](https://developer.android.com/studio/releases/gradle-plugin#updating-plugin) 4.0.0 or higher.

Then include the following in your app's `build.gradle` file:

```groovy
android {
  defaultConfig {
    // Required ONLY if your minSdkVersion is below 21
    multiDexEnabled true
  }

  compileOptions {
    // Enable support for the new language APIs
    coreLibraryDesugaringEnabled true
    // Set Java compatibility (version can be higher if desired)
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }

  kotlinOptions {
    // Also add this for Kotlin projects (version can be higher if desired)
    jvmTarget = "1.8"
  }
}

dependencies {
  coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:<latest-version>'
}
```

You can find the latest version of `desugar_jdk_libs` [here](https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs).

#### Step 2A - For pure Android projects without multiplatform setup

Add the desired calendar library (view or compose) to your app's `build.gradle.kts`:

```kotlin
dependencies {
  // The view calendar library for Android
  implementation("com.kizitonwose.calendar:view:<latest-version>")

  // The compose calendar library for Android
  implementation("com.kizitonwose.calendar:compose:<latest-version>")
}
```

#### Step 2B - For Compose Multiplatform projects

Add the multiplatform calendar library to your project's `build.gradle.kts`:

```kotlin
commonMain.dependencies {
  // The calendar library for compose multiplatform projects
  // Supports Android, iOS, js, WasmJs and Desktop platforms
  implementation("com.kizitonwose.calendar:compose-multiplatform:<latest-version>")
}
```

You can find the latest version of the library on the maven central badge above.

Snapshots of the development version are available in [Central Portal Snapshots repository](https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/com/kizitonwose/calendar/).

#### Compose UI version compatibility

For the compose calendar library, ensure that you are using the library version that matches the Compose UI version in your project. If you use a version of the library that has a higher version of Compose UI than the one in your project, gradle will upgrade the Compose UI version in your project via transitive dependency.

| Compose UI | Android Calendar Library | Multiplatform Calendar Library |
|:----------:|:------------------------:|:------------------------------:|
|   1.2.x    |          2.0.x           |               -                |
|   1.3.x    |      2.1.x - 2.2.x       |               -                |
|   1.4.x    |          2.3.x           |               -                |
|   1.5.x    |          2.4.x           |               -                |
|   1.6.x    |          2.5.x           |             2.5.x              |
|   1.7.x    |          2.6.x           |             2.6.x              |
|   1.8.x    |      2.7.x - 2.8.x       |         2.7.x - 2.8.x          |
|   1.9.x    |          2.9.x           |             2.9.x              |
|   1.10.x   |          2.10.x          |             2.10.x             |
|   1.11.x   |          2.10.x          |             2.10.x             |

## Usage

You can find the relevant documentation for the library in the links below.

|[View-based documentation](https://github.com/kizitonwose/Calendar/blob/main/docs/View.md)|[Compose documentation](https://github.com/kizitonwose/Calendar/blob/main/docs/Compose.md)|
|:-:|:-:|

## Migration

If you're upgrading from calendar library version 1.x.x to 2.x.x, see the [migration guide](https://github.com/kizitonwose/calendar/blob/main/docs/MigrationGuide.md).

## Share your creations

Made a cool calendar with this library? Share an image [here](https://github.com/kizitonwose/Calendar/issues/1).

## Contributing

Found a bug? feel free to fix it and send a pull request or [open an issue](https://github.com/kizitonwose/Calendar/issues).

## License

Calendar library is distributed under the MIT license.
See [LICENSE](https://github.com/kizitonwose/Calendar/blob/main/LICENSE.md) for details.

---

### Compose (`docs/Compose.md`)

# Calendar Compose Documentation

## Table of Contents

- [Quick links](#quick-links)
- [Compose Multiplatform Information](#compose-multiplatform-information)
- [Compose UI version compatibility](#compose-ui-version-compatibility)
- [Calendar Composables](#calendar-composables)
- [Usage](#usage)
  * [Calendar state](#usage)
  * [First day of the week](#first-day-of-the-week-and-day-of-week-titles)
  * [Headers and footers](#month-headers-and-footers)
  * [Calendar containers](#calendar-containers)
  * [Composable parameters](#other-composable-parameters)
  * [State properties](#state-properties)
  * [State methods](#state-methods)
  * [Date clicks](#date-clicks)
  * [Date Selection](#date-selection)
  * [Disabling dates](#disabling-dates)
- [Week calendar](#week-calendar)
- [HeatMap calendar](#heatmap-calendar)
- [Year calendar](#year-calendar)

## Quick links

Check out the sample app if you have not yet done so. Most techniques that you would want to implement are already done in the examples.

Download the Android sample app [here](https://github.com/kizitonwose/Calendar/releases/download/2.5.4/sample.apk)

Read the Android sample app's source code [here](https://github.com/kizitonwose/Calendar/tree/main/sample)

View the multiplatform sample project online at https://calendar.kizitonwose.dev

Read the multiplatform sample project's source code [here](https://github.com/kizitonwose/Calendar/tree/main/compose-multiplatform/sample)

Add the library to your project [here](https://github.com/kizitonwose/Calendar#setup)

**If you are looking for the view-based documentation, you can find it [here](https://github.com/kizitonwose/Calendar/blob/main/docs/View.md)**

## Compose Multiplatform Information

The APIs for the compose libraries for Android and Multiplatform projects have been designed such that you can copy examples across both projects and they would work without code changes as the classes have the same names and package declarations. The only difference in some cases would be that the code for the Android calendar library needs to import classes such as `LocalDate`, `YearMonth` and `Year` from the `java.time` package while the multiplaform calendar library needs to import such classes from the [kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) library. 

Note that the `Year` class does not yet exist in the `kotlinx-datetime` library, therefore the multiplatform calendar library includes a minimal `Year` class implementation to bridge this gap.

## Compose UI version compatibility

Ensure that you are using the library version that matches the Compose UI version in your project. If you use a version of the library that has a higher version of Compose UI than the one in your project, gradle will upgrade the Compose UI version in your project via transitive dependency. See the compatibility table [here](https://github.com/kizitonwose/Calendar#compose-ui-version-compatibility).

## Calendar Composables

The library can be used via six composables:

`HorizontalCalendar()`: Horizontally scrolling month-based calendar.

`VerticalCalendar()`: Vertically scrolling month-based calendar.

`WeekCalendar()`: Horizontally scrolling week-based calendar.

`HeatMapCalendar()`: Horizontally scrolling heatmap calendar, useful for showing how data changes over time. A popular example is the user contribution chart on GitHub.

`HorizontalYearCalendar()`: Horizontally scrolling year-based calendar.

`VerticalYearCalendar()`: Vertically scrolling year-based calendar.

All composables are based on LazyRow/LazyColumn for efficiency.

In the examples below, we will mostly use the month-based `HorizontalCalendar`
and `VerticalCalendar` composables since all the calendar composables share the same basic concept.
If you need a week-based calendar, use the `WeekCalendar` composable instead. If you need a
year-based calendar, use the `HorizontalYearCalendar` and `VerticalYearCalendar` composables.

Most state properties/methods with the name prefix/suffix `month` (e.g `firstVisibleMonth`) in the
month-base calendar will have an equivalent with the name prefix/suffix `week` (
e.g `firstVisibleWeek`) in the week-based calendar and `year` (e.g `firstVisibleYear`) in the
year-based calendar.

## Usage

`HorizontalCalendar` and `VerticalCalendar`:

```kotlin
@Composable
fun MainScreen() {
    val currentMonth = remember { YearMonth.now() }
    val startMonth = remember { currentMonth.minusMonths(100) } // Adjust as needed
    val endMonth = remember { currentMonth.plusMonths(100) } // Adjust as needed
    val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library

    val state = rememberCalendarState(
        startMonth = startMonth,
        endMonth = endMonth,
        firstVisibleMonth = currentMonth,
        firstDayOfWeek = firstDayOfWeek
    )

    HorizontalCalendar(
        state = state,
        dayContent = { Day(it) }
    )

//    If you need a vertical calendar.
//    VerticalCalendar(
//        state = state,
//        dayContent = { Day(it) }
//    )  
}
```

Note: There is an additional parameter that can be provided when creating the state: `outDateStyle`. This determines how the out-dates are generated. See the [properties](#state-properties) section to understand this parameter.

`WeekCalendar`:

```kotlin
@Composable
fun MainScreen() {
    val currentDate = remember { LocalDate.now() }
    val currentMonth = remember { YearMonth.now() }
    val startDate = remember { currentMonth.minusMonths(100).atStartOfMonth() } // Adjust as needed
    val endDate = remember { currentMonth.plusMonths(100).atEndOfMonth() } // Adjust as needed
    val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library

    val state = rememberWeekCalendarState(
        startDate = startDate,
        endDate = endDate,
        firstVisibleWeekDate = currentDate,
        firstDayOfWeek = firstDayOfWeek
    )

    WeekCalendar(
        state = state,
        dayContent = { Day(it) }
    )
}
```

`HorizontalYearCalendar` and `VerticalYearCalendar`:

```kotlin
@Composable
fun MainScreen() {
    val currentYear = remember { Year.now() }
    val startYear = remember { currentYear.minusYears(100) } // Adjust as needed
    val endYear = remember { currentYear.plusYears(100) } // Adjust as needed
    val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library

    val state = rememberYearCalendarState(
        startYear = startYear,
        endYear = endYear,
        firstVisibleYear = currentYear,
        firstDayOfWeek = firstDayOfWeek,
    )
    HorizontalYearCalendar(
        state = state,
        dayContent = { Day(it) },
    )

//    If you need a vertical year calendar.
//    VerticalYearCalendar(
//        state = state,
//        dayContent = { Day(it) }
//    )
}
```

Your `Day` composable in its simplest form would be:

```kotlin
@Composable
fun Day(day: CalendarDay) {
    Box(
        modifier = Modifier
            .aspectRatio(1f), // This is important for square sizing!
        contentAlignment = Alignment.Center
    ) {
        Text(text = day.date.dayOfMonth.toString())
    }
}
```

In the above example, we use `Modifier.aspectRatio(1f)`, this is because the calendar assigns the width of the month divided by 7 as the width of each day cell. No height is set, so you have the flexibility to decide what works best for you. 
To get the typical square look on a calendar, you use `Modifier.aspectRatio(1f)` which tells the box to make its height the same size as the assigned width.

You can choose to set a specific height if you want. For example: `Modifier.height(70.dp)`

**And that's all you need for simple usage! But keep reading, there's more!**

### First day of the week and Day of week titles.

Of course, you want to show the day of week titles on the appropriate days on the calendar.

`Sun | Mon | Tue | Wed | Thu | Fri | Sat`

Here's a method that generates the weekdays from the user's current Locale. 

```kotlin
val daysOfWeek = daysOfWeek() // Available in the library
```

The function takes a `firstDayOfWeek` parameter in case you want to generate the days of week such that the desired day is at the first position.

For example:

```kotlin
val daysOfWeek = daysOfWeek(firstDayOfWeek = DayOfWeek.THURSDAY)
// Will produce => Thu | Fri | Sat | Sun | Mon | Tue | Wed 
```
Using the `daysOfWeek` list, you can set up the calendar so the first day of the week is what the user would expect. This could be Sunday, Monday, etc. It is good practice to use what the Locale returns as that's what the user would expect.

To set up the calendar state using the provided `daysOfWeek` list:

```diff
- val firstDayOfWeek = remember { firstDayOfWeekFromLocale() }
+ val daysOfWeek = remember { daysOfWeek() }
  val state = rememberCalendarState(
      startMonth = startMonth,
      endMonth = endMonth,
      firstVisibleMonth = currentMonth,
-     firstDayOfWeek = firstDayOfWeek
+     firstDayOfWeek = daysOfWeek.first()
  )
```

You should also use the `daysOfWeek` list values to set up the weekday titles, this way it matches what is shown on the calendar.

To set up the day of week titles, you can either use the month header which would show the titles on every month and allow the titles to scroll with the month, or you can show the title on a static composable above the calendar. Both ways are covered below:

Setup days of week using a static title composable:

```kotlin
@Composable
fun DaysOfWeekTitle(daysOfWeek: List<DayOfWeek>) {
    Row(modifier = Modifier.fillMaxWidth()) {
        for (dayOfWeek in daysOfWeek) {
            Text(
                modifier = Modifier.weight(1f),
                textAlign = TextAlign.Center,
                text = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()),
            )
        }
    }
}
```

Now you can use the title composable with the calendar in a column:

```kotlin
@Composable
fun MainScreen() {
    Column {
        DaysOfWeekTitle(daysOfWeek = daysOfWeek) // Use the title here
        HorizontalCalendar(
            state = state,
            dayContent = { Day(it) }
        )
    }
}
```

To use the titles as a month header so it scrolls with each month, continue to the month headers and footers section below!

### Month headers and footers.

To add a header or footer to each month, the procedure is the same as we did for the day using the `dayContent` calendar parameter, but instead of the `dayContent`, you have to provide the `monthHeader` or `monthFooter` composable parameter.

To add the days of week titles as the month header, we can set the same `DaysOfWeekTitle` composable discussed above as the `monthHeader` parameter:

```kotlin
@Composable
fun MainScreen() {
    HorizontalCalendar(
        state = state,
        dayContent = { Day(it) },
        monthHeader = {
            DaysOfWeekTitle(daysOfWeek = daysOfWeek) // Use the title as month header
        }
    )
}
```

In the code above, we use the same `daysOfWeek` list that was created when we initialized the calendar state. However, we can also get the `daysOfWeek` list from the month data passed into the `monthHeader` parameter:

```kotlin
@Composable
fun MainScreen() {
    HorizontalCalendar(
        state = state,
        dayContent = { Day(it) },
        monthHeader = { month ->
            // You may want to use `remember {}` here so the mapping is not done 
            // every time as the days of week order will never change unless 
            // you set a new value for `firstDayOfWeek` in the state.
            val daysOfWeek = month.weekDays.first().map { it.date.dayOfWeek }
            MonthHeader(daysOfWeek = daysOfWeek)
        }
    )
}
```

With the day of week titles added, you can have a calendar that looks like this:

<img src="https://user-images.githubusercontent.com/15170090/195415979-b9e46c16-3652-433e-a85d-e1d05c25ca8b.png" alt="Month calendar" width="300">

You can do more than just use the day titles as the header. For example, you can also show the month name if it is not already shown somewhere outside the calendar. Feel free to get creative with your month headers and footers! For complex usages, please see the sample project.

### Calendar containers

Two interesting parameters of the calendar composable are the `monthBody` and `monthContainer`. You will typically not need these. But if you want to do some customizations before the calendar is rendered, then this is the place for it.

For example, if you want to draw a gradient behind the container where all the days are rendered and add rounded corners/borders to the entire month container and also shrink the entire month container so it does not fit the screen width, the `monthBody` and `monthContainer` will be:

```kotlin
@Composable
fun MainScreen() {
    HorizontalCalendar(
        // Draw the day content gradient.
        monthBody = { _, content ->
            Box(
                modifier = Modifier.background(
                    brush = Brush.verticalGradient(
                        colors = listOf(
                            Color(0xFFB2EBF2),
                            Color(0xFFB2B8F2)
                        )
                    )
                )
            ) {
                content() // Render the provided content!
            }
        },
        // Add the corners/borders and month width.
        monthContainer = { _, container ->
            val configuration = LocalConfiguration.current
            val screenWidth = configuration.screenWidthDp.dp
            Box(
                modifier = Modifier
                    .width(screenWidth * 0.73f)
                    .padding(8.dp)
                    .clip(shape = RoundedCornerShape(8.dp))
                    .border(
                        color = Color.Black,
                        width = 1.dp,
                        shape = RoundedCornerShape(8.dp)
                    )
            ) {
                container() // Render the provided container!
            }
        }
    )
}
```

With the above `monthBody` and `monthContainer` configurations, we will have this calendar:

<img src="https://user-images.githubusercontent.com/15170090/195417341-fc263e3c-7468-47f0-84be-db6a76e29f8f.png" alt="Background styles" width="250">

### Other composable parameters.

- **calendarScrollPaged**: The scrolling behavior of the calendar. When `true`, the calendar will snap to the nearest month after a scroll or swipe action. When `false`, the calendar scrolls normally.

- **userScrollEnabled**: Whether scrolling via the user gestures or accessibility actions is allowed. You can still scroll programmatically using the state even when it is disabled. Inherited from LazyRow/LazyColumn.

- **reverseLayout**: reverse the direction of scrolling and layout. When `true`, months will be composed from the end to the start and `startMonth` will be located at the end. Inherited from LazyRow/LazyColumn.

- **contentPadding**: A padding around the whole calendar. This will add padding for the content after it has been clipped, which is not possible via `modifier` parameter. Inherited from LazyRow/LazyColumn.

### State properties

All properties set when creating the state via `rememberCalendarState()` or `rememberWeekCalendarState()` can be updated in the future via the appropriate property in the state object. There are also other interesting properties in the state objects worth mentioning.

**`CalendarState` properties for `HorizontalCalendar` and `VerticalCalendar`:**

- **firstVisibleMonth**: The first month that is visible on the calendar.

- **lastVisibleMonth**: The last month that is visible on the calendar.

- **layoutInfo**: A subclass of `LazyListLayoutInfo` calculated during the last layout pass. For example, you can use it to calculate what items are currently visible.

- **isScrollInProgress**: Whether this calendar is currently scrolling by gesture, fling, or programmatically.

- **outDateStyle**: This determines how outDates are generated for each month on the calendar. It can be one of two values:
    1. **EndOfRow**: The calendar will generate `outDates` until it reaches the end of the month row. This means that if a month has 5 rows, it will display 5 rows and if a month has 6 rows, it will display 6 rows.
    2. **EndOfGrid**: The calendar will generate `outDates` until it reaches the end of a 6 x 7 grid on each month. This means that all months will have 6 rows.

    This value can also be provided when the calendar state is initialized via `rememberCalendarState(outDateStyle = ...)`.

If you are wondering what `outDates` and `inDates` mean, let's use the screenshot below as an example.

<img src="https://user-images.githubusercontent.com/15170090/197358602-c9c6f796-fb28-4c82-9101-458d7a66f3a0.png" alt="in-dates and out-dates" width="300">

In the image, the dates within the green annotation are `inDates`, the ones within the red annotation are `outDates` while those without annotation are `monthDates`. You can check for this when your calendar day is rendered. To achieve the exact effect on the image, we update our `Day` composable: 

```kotlin
@Composable
fun Day(day: CalendarDay) {
    Box(
        modifier = Modifier
            .aspectRatio(1f),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = day.date.dayOfMonth.toString(),
            color = if (day.position == DayPosition.MonthDate) Color.White else Color.Gray
        )
    }
}
```

`inDates` have their `position` property set to `DayPosition.InDate`

`outDates` have their `position` property set to `DayPosition.OutDate`

`monthDates` have their `position` property set to `DayPosition.MonthDate` as seen in the code snippet above.

**`WeekCalendarState` properties for `WeekCalendar`:**

- **firstVisibleWeek**: The first week that is visible on the calendar.

- **lastVisibleWeek**: The last week that is visible on the calendar.

- **layoutInfo**: A subclass of `LazyListLayoutInfo` calculated during the last layout pass. For example, you can use it to calculate what items are currently visible.

- **isScrollInProgress**: Whether this calendar is currently scrolling by gesture, fling, or programmatically.

**`YearCalendarState` properties for `HorizontalYearCalendar` and `VerticalYearCalendar`:**

- **firstVisibleYear**: The first year that is visible on the calendar.

- **lastVisibleYear**: The last year that is visible on the calendar.

- **layoutInfo**: A subclass of `LazyListLayoutInfo` calculated during the last layout pass. For example, you can use it to calculate what items are currently visible.

- **isScrollInProgress**: Whether this calendar is currently scrolling by gesture, fling, or programmatically.

### State methods

**`CalendarState`**

- **scrollToDate(date: LocalDate)**: Instantly scroll to a date on the calendar without an animation.

- **animateScrollToDate(date: LocalDate)**: Scroll to a date on the calendar with smooth scrolling animation.

- **scrollToMonth(month: YearMonth)**: Instantly scroll to a month on the calendar without an animation.

- **animateScrollToMonth(month: YearMonth)**: Scroll to a month on the calendar with smooth scrolling animation.

**`WeekCalendarState`**

- **scrollToDate(date: LocalDate)**: Instantly scroll to a date on the calendar without an animation.

- **animateScrollToDate(date: LocalDate)**: Scroll to a date on the calendar with smooth scrolling animation.

- **scrollToWeek(date: LocalDate)**: Instantly scroll to the week containing the given date on the calendar without an animation.

- **animateScrollToWeek(date: LocalDate)**: Scroll to the week containing the given date on the calendar with smooth scrolling animation.

**`YearCalendarState`**

- **scrollToDate(date: LocalDate)**: Instantly scroll to a date on the calendar without an animation.

- **animateScrollToDate(date: LocalDate)**: Scroll to a date on the calendar with smooth scrolling animation.

- **scrollToMonth(month: YearMonth)**: Instantly scroll to a month on the calendar without an animation.

- **animateScrollToMonth(month: YearMonth)**: Scroll to a month on the calendar with smooth scrolling animation.

- **scrollToYear(year: Year)**: Instantly scroll to a year on the calendar without an animation.

- **animateScrollToYear(year: Year)**: Scroll to a year on the calendar with smooth scrolling animation.

There's no need to repeat the documentation here. Please see the relevant class for all properties and methods available with proper documentation.

### Date clicks

You can handle clicks in your `Day` composable as you would for any other composable via the modifier: 

```kotlin
@Composable
fun Day(day: CalendarDay, onClick: (CalendarDay) -> Unit) {
    Box(
        modifier = Modifier
            .aspectRatio(1f)
            .clickable(
                enabled = day.position == DayPosition.MonthDate,
                onClick = { onClick(day) }
            ),
        contentAlignment = Alignment.Center
    ) {
        Text(text = day.date.dayOfMonth.toString())
    }
}
```

### Date Selection

The library has no inbuilt concept of selected/unselected dates, this gives you the freedom to choose how best you would like to implement this use case.

Implementing date selection is as simple as showing a background on a specific date in the `Day` composable. 

For this example, I want only the last clicked date to be selected on the calendar.

Firstly, we update our `Day` composable to show a circle background if the date is selected:

```kotlin
@Composable
fun Day(day: CalendarDay, isSelected: Boolean, onClick: (CalendarDay) -> Unit) {
    Box(
        modifier = Modifier
            .aspectRatio(1f)
            .clip(CircleShape)
            .background(color = if (isSelected) Color.Green else Color.Transparent)
            .clickable(
                enabled = day.position == DayPosition.MonthDate,
                onClick = { onClick(day) }
            ),
        contentAlignment = Alignment.Center
    ) {
        Text(text = day.date.dayOfMonth.toString())
    }
}
```

Next, using the click logic already shown in the date click section above, we update the selected date state whenever a date is clicked:

```kotlin
@Composable
fun MainScreen() {
    var selectedDate by remember { mutableStateOf<LocalDate?>(null) }
    HorizontalCalendar(
        state = state,
        dayContent = { day ->
            Day(day, isSelected = selectedDate == day.date) { day ->
                selectedDate = if (selectedDate == day.date) null else day.date
            }
        }
    )
}
```

For more complex selection logic like range selection, please see the sample project. It's quite simple, the magic is all in your logic!

### Disabling dates

As expected, the library does not provide this logic internally so you have complete flexibility.

To disable dates, you can simply set the texts on those dates to look disabled and disable clicks on those dates. For example, if we want to show in and out dates but disable them so that they cannot be selected, we can just set a different color on the texts.

We actually already did this with the example in the date click section, we already ignore clicks for in and out dates using this logic:

```kotlin
@Composable
fun Day(day: CalendarDay, onClick: (CalendarDay) -> Unit) {
    Box(
        modifier = Modifier
            .aspectRatio(1f)
            .clickable(
                enabled = day.position == DayPosition.MonthDate, // Only month-dates are clickable.
                onClick = { onClick(day) }
            ),
        contentAlignment = Alignment.Center
    ) { // Change the color of in-dates and out-dates, you can also hide them completely!
        Text(
            text = day.date.dayOfMonth.toString(),
            color = if (day.position == DayPosition.MonthDate) Color.White else Color.Gray
        )
    }
}
```

And we've now covered the typical usage. The beauty of the library is in its limitless possibilities. You are not constrained on how to build your user interface, the library provides you with the needed calendar data logic and you provide the desired UI logic. 

See the sample project for some complex implementations.

## Week calendar

The `WeekCalendar` is a week-based calendar. Almost all topics covered above for the month calendar
will apply to the week calendar. The main difference is that state properties/methods will have a
slightly different name, typically with a `week` prefix/suffix instead of `month`.

For example: `firstVisibleMonth` => `firstVisibleWeek`, `scrollToMonth()` => `scrollToWeek()` and many others, but you get the idea.

We already showed how to use the `WeekCalendar` previously in the [usage](#usage) section, but in the most basic form, it is:

```kotlin
@Composable
fun MainScreen() {
    val state = rememberWeekCalendarState(
        startDate = ...,
        endDate = ...,
        firstVisibleWeekDate = ...,
        firstDayOfWeek = ...
    )
    WeekCalendar(
        state = state,
        dayContent = { Day(it) }
    )  
}
```

A week calendar implementation from the sample app:

<img src="https://user-images.githubusercontent.com/15170090/195638551-dfced7be-c18f-4611-b015-cfefab480cee.png" alt="Week calendar" width="250">

If you would like to toggle the calendar between month and week modes, please see the sample app where we did this by animating the Modifier height and alternatively using the `AnimatedVisibility` API.

## HeatMap calendar

This is a horizontally scrolling heatmap calendar implementation, useful for showing how data changes over time. A popular example is the user contribution chart on GitHub. Another usage could be to show changes in the frequency of a habit tracked by a user.

A screenshot from the sample app is shown below:

<img src="https://user-images.githubusercontent.com/15170090/195638552-4c25cf23-d311-4d95-bff0-f1917f4bab8b.png" alt="HeatMap calendar" width="250">

All the properties in the month-based calendar are also available in the HeatMap calendar except `OutDateStyle` configuration as that is not relevant in this case. Note that there are out-dates on the calendar but since the dates are laid out in columns instead of rows, the two `OutDateStyle` options `EndOfRow` and `EndOfGrid` are not needed here. All other month-based properties are available!

Basic HeatMap calendar usage:

```kotlin
@Composable
fun MainScreen() {
    val currentMonth = remember { YearMonth.now() }
    val startMonth = remember { currentMonth.minusMonths(100) } // Adjust as needed
    val endMonth = remember { currentMonth.plusMonths(100) } // Adjust as needed
    val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library

    val state = rememberHeatMapCalendarState(
        startMonth = startMonth,
        endMonth = startMonth,
        firstVisibleMonth = currentMonth,
        firstDayOfWeek = firstDayOfWeek,
    )
    HeatMapCalendar(
        state = state,
        dayContent = { day, _ -> Day(day) },
        weekHeader = { WeekHeader(it) },
        monthHeader = { MonthHeader(it) }
    )
}
```

Please see the `HeatMapCalendar` composable for the full documentation. There are also examples in the sample app.

## Year calendar

The year-based calendar is best suited for large screens and can be used via
the `HorizontalYearCalendar` and `VerticalYearCalendar` composables. All topics covered above for
the month calendar will apply to the year calendar. The main difference is that state
properties/methods will have a slightly different name, typically with a `year` prefix/suffix
instead of `month`.

For example: `firstVisibleMonth` => `firstVisibleYear`, `scrollToMonth()` => `scrollToYear()` and
many others, but you get the idea.

The `monthHeader` and `monthFooter` parameters are available in both the month and year calendars
and serve the same purpose in both cases. The year calendar additionally provides the `yearHeader`
and `yearFooter` parameters to add a header or footer to each year on the calendar.

Basic year calendar usage:

```kotlin
@Composable
fun MainScreen() {
    val currentYear = remember { Year.now() }
    val startYear = remember { currentYear.minusYears(100) } // Adjust as needed
    val endYear = remember { currentYear.plusYears(100) } // Adjust as needed
    val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library

    val state = rememberYearCalendarState(
        startYear = startYear,
        endYear = endYear,
        firstVisibleYear = currentYear,
        firstDayOfWeek = firstDayOfWeek,
    )
    HorizontalYearCalendar(
        state = state,
        dayContent = { Day(it) },
        yearHeader = { YearHeader(it) },
        monthHeader = { MonthHeader(it) },
    )

//    If you need a vertical year calendar.
//    VerticalYearCalendar(
//        state = state,
//        dayContent = { Day(it) }
//    )
}
```

There is an additional `outDateStyle` parameter that can be provided when creating the state
via `rememberYearCalendarState`. This determines how the out-dates are generated. See
the [properties](#state-properties) section to understand this parameter.

A year calendar implementation from the sample app:

<img src="https://github.com/user-attachments/assets/b2fcae94-341c-4f35-a997-e8d95a23efb4" alt="Year calendar" width="500">

The year calendar composables also provide a parameter `isMonthVisible` which determines if a month
is added to the calendar year grid. For example, if you want a calendar that starts in the year 2024
and ends in the year 2054, but only shows months from October 2024, the logic would look like
this:

```kotlin
@Composable
fun MainScreen() {
    val october2024 = remember { YearMonth.of(2024, Month.OCTOBER) }
    val startYear = remember { Year.of(2024) }
    val endYear = remember { Year.of(2054) }
    val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } 

    val state = rememberYearCalendarState(
        startYear = startYear,
        endYear = endYear,
        firstVisibleYear = startYear,
        firstDayOfWeek = firstDayOfWeek,
    )
    HorizontalYearCalendar(
        state = state,
        dayContent = { Day(it) },
        yearHeader = { YearHeader(it) },
        monthHeader = { MonthHeader(it) },
        isMonthVisible = { data ->
            data.yearMonth >= october2024
        }
    )
}
```

The logic above will produce this result:

<img src="https://github.com/user-attachments/assets/80ff60c0-91c3-4d6f-89d5-c719d1981c0d" alt="Year calendar" width="500">

Remember that all the screenshots shown so far are just examples of what you can achieve with the library and you can absolutely build your calendar to look however you want.

**Made a cool calendar with this library? Share an image [here](https://github.com/kizitonwose/Calendar/issues/1).**

---

### MigrationGuide (`docs/MigrationGuide.md`)

# Migration Guide

- [View](#view)
  * [1.x.x => 2.x.x](#1xx--2xx)
  * [0.3.x => 0.4.x / 1.x.x](#03x--04x--1xx)
- [Compose](#compose)

## View

### 1.x.x => 2.x.x

If you're upgrading from version `1.x.x` to `2.x.x`, please keep in mind that version 2 is a total rewrite of the library with focus on performance. With that in mind, here are the breaking changes:

- `async()` methods have been removed as the calendar is now capable of handling really large date ranges without performance issues. The calendar data is generated as needed. The removed methods are `setupAsync()` and `updateMonthRangeAsync()`. Please use the methods `setup()` and `updateMonthData()`.

- `updateMonthRange()` method has been renamed to `updateMonthData()` as it now allows you to optionally change the first day of the week if needed.

- `daySize` property is no longer the `Size` class type with `height` and `width` values. It is now a `DaySize` enum with three options - `Square`, `SeventhWidth` and `FreeForm`. Please see the `DaySize` enum documentation to understand each value.

- `inDateStyle` enum property has been removed as it is now redundant. The in-dates are present in each month data, you can choose to hide them on the view or keep them.

- `outDateStyle` enum property is still available. However, there are now two options `EndOfRow` and `EndOfGrid`. The previously available third option `None` has been removed. You can simply hide the out-dates if you don't need them.

- `scrollMode` enum property is now a boolean type named `scrollPaged`. Set this property to `false` to have the previous `ScrollMode.CONTINUOUS` scroll behavior or `true` to have the previous `ScrollMode.PAGED` behavior.

- `DayOwner` enum has been renamed to `DayPosition`. The matching case values are:
    - `DayOwner.PREVIOUS_MONTH` => `DayPosition.InDate`
    - `DayOwner.THIS_MONTH` => `DayPosition.MonthDate`
    - `DayOwner.NEXT_MONTH` => `DayPosition.OutDate`

- `maxRowCount` property has been removed as there is now a `WeekCalendarView` class that should be used if a week-based calendar is needed. The main `CalendarView` class is used for the month calendar implementation.

- `hasBoundaries` property has been removed as it is no longer needed with the introduction of the week and month calendar implementations discussed above.

- `monthMarginStart` | `monthMarginTop` | `monthMarginEnd` | `monthMarginBottom` properties have been merged into one `monthMargins` property.

- `monthPaddingStart` | `monthPaddingTop` | `monthPaddingEnd` | `monthPaddingBottom` properties have been removed as they provided no real benefit. You can create a custom `monthViewClass` and set the paddings there if needed.


### 0.3.x => 0.4.x / 1.x.x

If you're upgrading from version `0.3.x` to `0.4.x` or `1.x.x`, the main change is that the library moved from using [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) to [Java 8 API desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) for dates. After following the new [setup](https://github.com/kizitonwose/Calendar#setup) instructions, the next thing you need to do is change your imports for date/time related classes from `org.threeten.bp.*` to `java.time.*`.

You also need to remove the line `AndroidThreeTen.init(this)` from the `onCreate()` method of your application class as it's no longer needed.


## Compose

Nothing to see here... yet 😉

---

### View (`docs/View.md`)

# Calendar View Documentation

## Table of Contents

- [Quick links](#quick-links)
- [Class information](#class-information)
- [Usage](#usage)
  * [Setup](#step-1)
  * [First day of the week](#first-day-of-the-week-and-day-of-week-titles)
  * [Headers and footers](#month-headers-and-footers)
  * [Attributes](#attributes)
  * [Properties](#properties)
  * [Methods](#methods)
  * [Date clicks](#date-clicks)
  * [Date Selection](#date-selection)
  * [Disabling dates](#disabling-dates)
- [Week view](#week-view)
- [Year view](#year-view)
- [FAQ](#faq)
- [Migration](#migration)

## Quick links

Check out the sample app if you have not yet done so. Most techniques that you would want to implement are already done in the examples.

Download the sample app [here](https://github.com/kizitonwose/Calendar/releases/download/2.5.4/sample.apk)

Read the sample app's source code [here](https://github.com/kizitonwose/Calendar/tree/main/sample)

Add the library to your project [here](https://github.com/kizitonwose/Calendar#setup)

**If you are looking for the compose documentation, you can find it [here](https://github.com/kizitonwose/Calendar/blob/main/docs/Compose.md)**

## Class information

The library can be used via three classes: 

`CalendarView`: The typical month-based calendar.

`WeekCalendarView`: The week-based calendar.

`YearCalendarView`: The year-based calendar.

These classes extend from `RecyclerView` so you can use all `RecyclerView` customizations like decorators etc.

In the examples below, we will mostly use the `CalendarView` class since the three classes share the same basic concept. If you want a week-based calendar, replace `CalendarView` in your xml/code with `WeekCalendarView`. If you want a year-based calendar, replace `CalendarView` in your xml/code with `YearCalendarView`. 

Most xml attributes and class properties/methods with the name prefix/suffix `month` (e.g `monthHeaderResource`) in the `CalendarView` will have an equivalent with the name prefix/suffix `week` (e.g `weekHeaderResource`) in the `WeekCalendarView` and the name prefix/suffix `year` (e.g `yearHeaderResource`) in the `YearCalendarView`.

## Usage

#### Step 1

Add CalendarView to your XML like any other view.

```xml
<com.kizitonwose.calendar.view.CalendarView
    android:id="@+id/calendarView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cv_dayViewResource="@layout/calendar_day_layout" />
```
See all available [attributes](#attributes).

Create your day view resource in `res/layout/calendar_day_layout.xml`.

```xml
<TextView
    android:id="@+id/calendarDayText"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:textSize="16sp"
    tools:text="22" />
```

Create your view container which acts as a view holder for each date cell.
The view passed in here is the inflated day view resource which you provided.

```kotlin
class DayViewContainer(view: View) : ViewContainer(view) {    
    val textView = view.findViewById<TextView>(R.id.calendarDayText)

    // With ViewBinding
    // val textView = CalendarDayLayoutBinding.bind(view).calendarDayText
}
```

Provide a `MonthDayBinder` for the CalendarView using your `DayViewContainer` type.

```kotlin
calendarView.dayBinder = object : MonthDayBinder<DayViewContainer> {
    // Called only when a new container is needed.
    override fun create(view: View) = DayViewContainer(view)
    
    // Called every time we need to reuse a container.
    override fun bind(container: DayViewContainer, data: CalendarDay) {
        container.textView.text = data.date.dayOfMonth.toString()
    }
}
```

#### Step 2

Setup the desired dates in your Fragment or Activity:

**`CalendarView` setup:**
```kotlin
val currentMonth = YearMonth.now()
val startMonth = currentMonth.minusMonths(100) // Adjust as needed
val endMonth = currentMonth.plusMonths(100) // Adjust as needed
val firstDayOfWeek = firstDayOfWeekFromLocale() // Available from the library
calendarView.setup(startMonth, endMonth, firstDayOfWeek)
calendarView.scrollToMonth(currentMonth)
```

**`WeekCalendarView` setup:**

```diff
- <com.kizitonwose.calendar.view.CalendarView
+ <com.kizitonwose.calendar.view.WeekCalendarView
    android:id="@+id/weekCalendarView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cv_dayViewResource="@layout/calendar_day_layout" />
```

```kotlin
val currentDate = LocalDate.now()
val currentMonth = YearMonth.now()
val startDate = currentMonth.minusMonths(100).atStartOfMonth() // Adjust as needed
val endDate = currentMonth.plusMonths(100).atEndOfMonth() // Adjust as needed
val firstDayOfWeek = firstDayOfWeekFromLocale() // Available from the library
weekCalendarView.setup(startDate, endDate, firstDayOfWeek)
weekCalendarView.scrollToWeek(currentDate)
```

**`YearCalendarView` setup:**

```diff
- <com.kizitonwose.calendar.view.CalendarView
+ <com.kizitonwose.calendar.view.YearCalendarView
    android:id="@+id/yearCalendarView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:cv_dayViewResource="@layout/calendar_day_layout" />
```

```kotlin
val currentYear = Year.now()
val startYear = currentYear.minusYears(100) // Adjust as needed
val endYear = currentYear.plusYears(100) // Adjust as needed
val firstDayOfWeek = firstDayOfWeekFromLocale() // Available from the library
yearCalendarView.setup(startYear, endYear, firstDayOfWeek)
yearCalendarView.scrollToYear(currentYear)
```

**And that's all you need for simple usage! But keep reading, there's more!**

### First day of the week and Day of week titles.

Of course, you want to show the day of week titles on the appropriate days on the calendar.

`Sun | Mon | Tue | Wed | Thu | Fri | Sat`

Here's a function that generates the weekdays from the user's current Locale. 

```kotlin
val daysOfWeek = daysOfWeek() // Available in the library
```

The function takes a `firstDayOfWeek` parameter in case you want to generate the days of week such that the desired day is at the first position.

For example:

```kotlin
val daysOfWeek = daysOfWeek(firstDayOfWeek = DayOfWeek.THURSDAY)
// Will produce => Thu | Fri | Sat | Sun | Mon | Tue | Wed 
```
Using the `daysOfWeek` list, you can set up the calendar so the first day of the week is what the user would expect. This could be Sunday, Monday, etc. It is good practice to use what the Locale returns as that's what the user would expect.

To set up the calendar using the provided `daysOfWeek` list:

```diff
- val firstDayOfWeek = firstDayOfWeekFromLocale()
+ val daysOfWeek = daysOfWeek()
  calendarView.setup(startMonth, endMonth, daysOfWeek.first())
```

You should also use the `daysOfWeek` list values to set up the weekday titles, this way it matches what is shown on the CalendarView.

To set up the day of week titles, you can either use the month header which would show the titles on every month and allow the titles to scroll with the month, or you can show the title on a static view above the calendar. Both ways are covered below:

Setup days of week using a static view:

#### Step 1
Create your title text view in `res/layout/calendar_day_title_text.xml`.

```xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:gravity="center" />
```

Create a container resource to use the text in `res/layout/calendar_day_titles_container.xml`.

```xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:weightSum="7"
    android:orientation="horizontal">
    
    <include layout="@layout/calendar_day_title_text" />

    <include layout="@layout/calendar_day_title_text" />

    <include layout="@layout/calendar_day_title_text" />

    <include layout="@layout/calendar_day_title_text" />

    <include layout="@layout/calendar_day_title_text" />

    <include layout="@layout/calendar_day_title_text" />

    <include layout="@layout/calendar_day_title_text" />

</LinearLayout>
```

Add the titles container in the same layout as the CalendarView:

```xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <include
        android:id="@+id/titlesContainer"
        layout="@layout/calendar_day_titles_container" />

    <com.kizitonwose.calendar.view.CalendarView
        android:id="@+id/calendarView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cv_dayViewResource="@layout/calendar_day_layout" />

</LinearLayout>
```

#### Step 2

Now you can set up the titles using the `daysOfWeek` list discussed previously:

```kotlin
val titlesContainer = findViewById<ViewGroup>(R.id.titlesContainer)
titlesContainer.children
    .map { it as TextView }
    .forEachIndexed { index, textView ->
        val dayOfWeek = daysOfWeek[index]
        val title = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault())
        textView.text = title
    }
```

To use the titles as a month header so it scrolls with each month, continue to the month headers and footers section below!

### Month headers and footers.

To add a header or footer to each month, the procedure is the same as we did for the day using the `dayViewResource` above, but instead of the day resource, you have to provide your `monthHeaderResource` or `monthFooterResource` attribute, then set the `monthHeaderBinder` or `monthFooterBinder` property of the CalendarView.

To add the days of week titles as the month header, we provide the title container as the header resource:

```xml
<com.kizitonwose.calendar.view.CalendarView
    android:id="@+id/calendarView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cv_dayViewResource="@layout/calendar_day_layout"
    app:cv_monthHeaderResource="@layout/calendar_day_titles_container" /> 

```

Now we can use the header to show the title:

```
/* Detailed source-code truncated for AI context efficiency. */
```

You can do more than just use the day titles as the header. For example, you can also show the month name if it is not already shown outside the calendar on a separate view. Feel free to get creative with your month headers and footers! For more complex usages, please see the sample project.

### Attributes

#### XML (All prefixed `cv_` for clarity)

**The following attributes are available for `CalendarView`, `WeekCalendarView` and `YearCalendarView` classes:**

- **dayViewResource**: The xml resource that is inflated and used as the day cell view. This must be provided.

- **scrollPaged**: The scrolling behavior of the calendar. If `true`, the calendar will snap to the nearest month or week (in the WeekCalendarView) after a scroll or swipe action. If `false`, the calendar scrolls normally.

- **daySize**: Determines how the size of each day on the calendar is calculated. Can be one of three values:
    1. **square**: Each day will have both width and height matching the width of the calendar divided by 7.
    2. **rectangle**: Each day will have its width matching the width of the calendar divided by 7, and its height matching the height of the calendar divided by the number of weeks in the index - could be 4, 5 or 6 for the month calendar, and 1 for the week calendar. 
    Use this if you want each month or week to fill the parent's width and height.
    2. **seventhWidth**: Each day will have its width matching the width of the calendar divided by 7. The day is allowed to determine its height by setting a specific value or using `LayoutParams.WRAP_CONTENT`
    3. **freeForm**: This day is allowed to determine its width and height by setting specific values or using `LayoutParams.WRAP_CONTENT`.

**The following attributes are available for `CalendarView` and `YearCalendarView` classes:**

- **monthHeaderResource**: The xml resource that is inflated and used as a header for each month.

- **monthFooterResource**: The xml resource that is inflated and used as a footer for each month.

- **orientation**: The calendar scroll direction, can be `horizontal` or `vertical`. Default is `horizontal`.

- **monthViewClass**: A ViewGroup that is instantiated and used as the container for each month. This class must have a constructor which takes only a Context. You should exclude the name and constructor of this class from code obfuscation if enabled.

- **outDateStyle**: This determines how outDates are generated for each month on the calendar. Can be one of two values:
    1. **endOfRow**: The calendar will generate `outDates` until it reaches the end of the month row. This means that if a month has 5 rows, it will display 5 rows and if a month has 6 rows, it will display 6 rows.
    2. **endOfGrid**: The calendar will generate `outDates` until it reaches the end of a 6 x 7 grid on each month. This means that all months will have 6 rows.

If you are wondering what `outDates` and `inDates` mean, let's use the screenshot below as an example.

<img src="https://user-images.githubusercontent.com/15170090/197358602-c9c6f796-fb28-4c82-9101-458d7a66f3a0.png" alt="in-dates and out-dates" width="300">

In the image, the dates within the green annotation are `inDates`, the ones within the red annotation are `outDates` while those without annotation are `monthDates`. You can check for this when binding your calendar. To achieve the exact effect on the image, we do this: 

```kotlin
calendarView.dayBinder = object : MonthDayBinder<DayViewContainer> {
    override fun create(view: View) = DayViewContainer(view)
    override fun bind(container: DayViewContainer, data: CalendarDay) {
        container.textView.text = data.date.dayOfMonth.toString()
        if (data.position == DayPosition.MonthDate) {
            container.textView.setTextColor(Color.WHITE)
        } else {
            container.textView.setTextColor(Color.GRAY)
        }
    }
}
```

`inDates` have their `position` property set to `DayPosition.InDate`

`outDates` have their `position` property set to `DayPosition.OutDate`

`monthDates` have their `position` property set to `DayPosition.MonthDate` as seen in the code snippet above.

**The following attributes are available for ONLY `WeekCalendarView` class:**

- **weekHeaderResource**: The xml resource that is inflated and used as a header for every week.

- **weekFooterResource**: The xml resource that is inflated and used as a footer for every week.

- **weekViewClass**: A ViewGroup that is instantiated and used as the container for each week. This class must have a constructor which takes only a Context. You should exclude the name and constructor of this class from code obfuscation if enabled.

**The following attributes are available for ONLY `YearCalendarView` class:**

- **yearHeaderResource**: The xml resource that is inflated and used as a header for each year.

- **yearFooterResource**: The xml resource that is inflated and used as a footer for each year.

- **yearViewClass**: A ViewGroup that is instantiated and used as the container for each year. This class must have a constructor which takes only a Context. You should exclude the name and constructor of this class from code obfuscation if enabled.

- **monthColumns**: The number of month columns in each year. Must be from 1 to 12.

- **monthHorizontalSpacing**: The horizontal spacing between month columns in each year.

- **monthVerticalSpacing**: The vertical spacing between month rows in each year.

- **monthHeight**: This determines how the height of each month row on the calendar is calculated. Can be one of two values:
    1. **followDaySize**: Each month row height is determined by the `daySize` value set on the calendar. Effectively, this is `wrap-content` if the value is `Square`,
    `SeventhWidth`, or `FreeForm`, and will be equal to the calendar height divided by the number of rows if the value is `Rectangle`. When used together with `Rectangle`, 
    the calendar months and days will uniformly stretch to fill the parent's height.
    2. **fill**: Each month row height will be the calendar height divided by the number of rows on the calendar. This means that the calendar months will be distributed
    uniformly to fill the parent's height. However, the day content height will independently determine its height. This allows you to spread the calendar months evenly across the screen while
    a `daySize` value of `Square` if you want square day content or `SeventhWidth` if you want to set a specific height value for the day content.

### Properties

All the respective XML attributes listed above are also available as properties of the CalendarView and WeekCalendarView classes so they can be set via code. So in addition to those, we have:

**`CalendarView`**

- **monthScrollListener**: Called when the calendar scrolls to a new month. Mostly beneficial if `scrollPaged` is `true`.

- **dayBinder**: An instance of `MonthDayBinder` for managing day cell views.

- **monthHeaderBinder**: An instance of `MonthHeaderFooterBinder` for managing header views. The header view is shown above each month on the calendar.

- **monthFooterBinder**: An instance of `MonthHeaderFooterBinder` for managing footer views. The footer view is shown below each month on the calendar.

- **monthMargins**: The margins, in pixels to be applied on each month view. This can be used to add a space between two months.

**`WeekCalendarView`**

- **weekScrollListener**: Called when the calendar scrolls to a new week. Mostly beneficial if `scrollPaged` is `true`.

- **dayBinder**: An instance of `WeekDayBinder` for managing day cell views.

- **weekHeaderBinder**: An instance of `WeekHeaderFooterBinder` for managing the header views shown above each week on the calendar.

- **weekFooterBinder**: An instance of `WeekHeaderFooterBinder` for managing the footer views shown below each week on the calendar.

- **weekMargins**: The margins, in pixels to be applied on each week view. This can be used to add a space between two weeks.

**`YearCalendarView`**

- **yearScrollListener**: Called when the calendar scrolls to a new year. Mostly beneficial if `scrollPaged` is `true`.

- **dayBinder**: An instance of `MonthDayBinder` for managing day cell views.

- **monthHeaderBinder**: An instance of `MonthHeaderFooterBinder` for managing the header views shown above each month on the calendar.

- **monthFooterBinder**: An instance of `MonthHeaderFooterBinder` for managing the footer views shown below each month on the calendar.

- **monthMargins**: The margins, in pixels to be applied on each month view. This can be used to add a space between two months.

- **yearHeaderBinder**: An instance of `YearHeaderFooterBinder` for managing the header views shown above each year on the calendar.

- **yearFooterBinder**: An instance of `YearHeaderFooterBinder` for managing the footer views shown below each year on the calendar.

- **yearMargins**: The margins, in pixels to be applied on each year view. This is the container in which the year header, body and footer are placed. For example, this can be used to add a space between two years.

- **yearBodyMargins**: The margins, in pixels to be applied on each year body view. This is the grid in which the months in each year are shown, excluding the year header and footer.

### Methods

**`CalendarView`**

- **scrollToDate(date: LocalDate)**: Scroll to a specific date on the calendar. Use `smoothScrollToDate()` to get a smooth scrolling animation. 

- **scrollToMonth(month: YearMonth)**: Scroll to a month on the calendar. Use `smoothScrollToMonth()` to get a smooth scrolling animation.

- **notifyDateChanged(date: LocalDate)**: Reload the view for the specified date.

- **notifyMonthChanged(month: YearMonth)**: Reload the header, body and footer views for the specified month.

- **notifyCalendarChanged()**: Reload the entire calendar.

- **findFirstVisibleMonth()** and **findLastVisibleMonth()**: Find the first and last visible months on the calendar respectively.

- **findFirstVisibleDay()** and **findLastVisibleDay()**: Find the first and last visible days on the calendar respectively.

- **updateMonthData()**: Update the calendar's start month or end month or the first day of week after the initial setup. The currently visible month is preserved. The calendar can handle really large date ranges so you may want to setup the calendar with a large date range instead of updating the range frequently.

**`WeekCalendarView`**

- **scrollToDate(date: LocalDate)**: Scroll to a specific date on the calendar. Use `smoothScrollToDate()` to get a smooth scrolling animation. 

- **scrollToWeek(date: LocalDate)**: Scroll to the week containing this date on the calendar. Use `smoothScrollToWeek()` to get a smooth scrolling animation.

- **notifyDateChanged(date: LocalDate)**: Reload the view for the specified date.

- **notifyWeekChanged(date: LocalDate)**: Reload the header, body and footer views for the week containing this date.

- **notifyCalendarChanged()**: Reload the entire calendar.

- **findFirstVisibleWeek()** and **findLastVisibleWeek()**: Find the first and last visible weeks on the calendar respectively.

- **findFirstVisibleDay()** and **findLastVisibleDay()**: Find the first and last visible days on the calendar respectively.

- **updateWeekData()**: Update the calendar's start date or end date or the first day of week after the initial setup. The currently visible week is preserved. The calendar can handle really large date ranges so you may want to setup the calendar with a large date range instead of updating the range frequently.

**`YearCalendarView`**

- **scrollToDate(date: LocalDate)**: Scroll to a specific date on the calendar. Use `smoothScrollToDate()` to get a smooth scrolling animation. 

- **scrollToMonth(month: YearMonth)**: Scroll to a month on the calendar. Use `smoothScrollToMonth()` to get a smooth scrolling animation.

- **scrollToYear(year: Year)**: Scroll to a year on the calendar. Use `smoothScrollToYear()` to get a smooth scrolling animation.

- **notifyDateChanged(date: LocalDate)**: Reload the view for the specified date.

- **notifyMonthChanged(month: YearMonth)**: Reload the header, body and footer views for the specified month.

- **notifyYearChanged(year: Year)**: Reload the header, body (all months in the year) and footer views for the specified year.

- **notifyCalendarChanged()**: Reload the entire calendar.

- **findFirstVisibleYear()** and **findLastVisibleYear()**: Find the first and last visible years on the calendar respectively.

- **findFirstVisibleMonth()** and **findLastVisibleMonth()**: Find the first and last visible months on the calendar respectively.

- **findFirstVisibleDay()** and **findLastVisibleDay()**: Find the first and last visible days on the calendar respectively.

- **updateYearData()**: Update the calendar's start year or end year or the first day of week after the initial setup. The currently visible year is preserved. The calendar can handle really large date ranges so you may want to setup the calendar with a large date range instead of updating the range frequently.

There's no need to list all available methods or repeat the documentation here. Please see
the [CalendarView](https://github.com/kizitonwose/Calendar/blob/main/view/src/main/java/com/kizitonwose/calendar/view/CalendarView.kt), [WeekCalendarView](https://github.com/kizitonwose/Calendar/blob/main/view/src/main/java/com/kizitonwose/calendar/view/WeekCalendarView.kt) and [YearCalendarView](https://github.com/kizitonwose/Calendar/blob/main/view/src/main/java/com/kizitonwose/calendar/view/YearCalendarView.kt)
classes for all properties and methods available with proper documentation.

### Date clicks

You should set a click listener on the view which is provided to the view container. 

XML file for the date cell `calendar_day_layout.xml`:

```xml

<TextView
    android:id="@+id/calendarDayText"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:textSize="16sp"
    tools:text="22" />
```

Of course, you need to set the file as `cv_dayViewResource` on the CalendarView:

```xml
<com.kizitonwose.calendarview.CalendarView
    android:id="@+id/calendarView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:cv_dayViewResource="@layout/calendar_day_layout" />
```

Click listener implementation in your Fragment or Activity:

```kotlin
class DayViewContainer(view: View) : ViewContainer(view) {
    val textView = view.findViewById<TextView>(R.id.calendarDayText)
    // Will be set when this container is bound
    lateinit var day: CalendarDay
    
    init {
        view.setOnClickListener {
            // Use the CalendarDay associated with this container.
        }
    }
}

calendarView.dayBinder = object : MonthDayBinder<DayViewContainer> {
    override fun create(view: View) = DayViewContainer(view)
    override fun bind(container: DayViewContainer, data: CalendarDay) {
        // Set the calendar day for this container.
        container.day = data
        // Set the date text
        container.textView.text = data.date.dayOfMonth.toString()
        // Any other binding logic
    }
}
```

### Date Selection

The library has no inbuilt concept of selected/unselected dates, this gives you the freedom to choose how best you would like to implement this use case.

Implementing date selection is as simple as showing a background on a specific date in the date binder. Remember that since CalendarView, WeekCalendarView and YearCalendarView all extend from RecyclerView, you need to undo any special effects on dates where it is not needed. 

For this example, I want only the last clicked date to be selected on the calendar.

Firstly, let's keep a reference to the selected date:

```kotlin
private var selectedDate: LocalDate? = null
```

Next, using the click logic on the view container already shown in the date click section above, we update this field whenever a date is clicked and show the selection background on the clicked date. 

```kotlin
view.setOnClickListener {
    // Check the day position as we do not want to select in or out dates.
    if (day.position == DayPosition.MonthDate) {
        // Keep a reference to any previous selection
        // in case we overwrite it and need to reload it.
        val currentSelection = selectedDate
        if (currentSelection == day.date) {
            // If the user clicks the same date, clear selection.
            selectedDate = null
            // Reload this date so the dayBinder is called
            // and we can REMOVE the selection background.
            calendarView.notifyDateChanged(currentSelection)
        } else {
            selectedDate = day.date
            // Reload the newly selected date so the dayBinder is
            // called and we can ADD the selection background.
            calendarView.notifyDateChanged(day.date)
            if currentSelection != null {
                // We need to also reload the previously selected 
                // date so we can REMOVE the selection background.
                calendarView.notifyDateChanged(currentSelection)
            }
        }
    }
}
```

Lastly, we implement the `dayBinder` to reflect the selection accordingly:

```kotlin
calendarView.dayBinder = object : MonthDayBinder<DayViewContainer> {
    override fun create(view: View) = DayViewContainer(view)
    override fun bind(container: DayViewContainer, data: CalendarDay) {
        container.day = data
        val day = data
        val textView = container.textView
        textView.text = day.date.dayOfMonth.toString()
        if (day.position == DayPosition.MonthDate) {
            // Show the month dates. Remember that views are reused!
            textView.visibility = View.VISIBLE
            if (day.date == selectedDate) {
                // If this is the selected date, show a round background and change the text color.
                textView.setTextColor(Color.WHITE)
                textView.setBackgroundResource(R.drawable.selection_background)
            } else {
                // If this is NOT the selected date, remove the background and reset the text color.
                textView.setTextColor(Color.BLACK)
                textView.background = null
            }
        } else {
            // Hide in and out dates
            textView.visibility = View.INVISIBLE
        }
    }
}
```

For more complex selection logic like range selection, please see the sample project. It's quite simple, the magic is all in your binding logic!

### Disabling dates

As expected, the library does not provide this logic internally so you have complete flexibility.

To disable dates, you can simply set the texts on those dates to look disabled and ignore clicks on those dates. For example, if we want to show in and out dates but disable them so that they cannot be selected, we can just set the alpha property for those dates in the `dayBinder` to give the effect of being disabled. Of course, you can set a different color if that's what you prefer.

Continuing with the example in the date selection section, we already ignore clicks for in and out dates using this logic:

```kotlin
view.setOnClickListener {
    // Check the day position as we do not want to select in or out dates.
    if (day.position == DayPosition.MonthDate) {
        // Only use month dates
    }
}
```

Then in the `dayBinder`, we check the day position and set the text alpha or color accordingly:

```kotlin
calendarView.dayBinder = object : MonthDayBinder<DayViewContainer> {
    override fun create(view: View) = DayViewContainer(view)
    override fun bind(container: DayViewContainer, data: CalendarDay) {
        container.day = data
        val textView = container.textView
        textView.text = data.date.dayOfMonth.toString()
        textView.alpha = if (day.position == DayPosition.MonthDate) 1f else 0.3f
}
```

And we've now covered the typical usage. The beauty of the library is in its limitless possibilities. You are not constrained on how to build your user interface, the library provides you with the needed calendar data logic and you provide the desired UI logic. 

See the sample project for some complex implementations.

## Week view

The `WeekCalendarView` class is a week-based calendar implementation. Almost all topics covered above for the month calendar will apply to the week calendar. The main difference is that the xml attributes and class properties/methods will have a slightly different name, typically with a `week` prefix/suffix instead of `month`. 

For example: `monthHeaderResource` => `weekHeaderResource`, `scrollToMonth()` => `scrollToWeek()`, `findFirstVisibleMonth()` => `findFirstVisibleWeek()` and many others, but you get the idea.

To show the week calendar in your layout, add the view:

```xml
<com.kizitonwose.calendar.view.WeekCalendarView
    android:id="@+id/weekCalendarView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cv_dayViewResource="@layout/calendar_day_layout" />
```

Then follow the setup instructions above to provide a day resource/binder etc as you would do for the month calendar.

If you would like to toggle the calendar between month and week views, please see the sample app where we did this using a `ValueAnimator`. You can use whichever animation logic you prefer. The result is shown below:

<img src="https://user-images.githubusercontent.com/15170090/195636303-a99312c9-23a3-44cd-8a38-6ba21b3c4802.gif" alt="Week and month modes" width="250">

If you wish to show more or less than 7 days at a time on the week calendar, you should set the `scrollPaged` attribute to `false`. Also, set the `daySize` property to `FreeForm` which gives you the freedom to define a preferred size for your day cells. Please read the documentation in the `DaySize` class to fully understand the available options.

A week calendar implementation from the sample app:

<img src="https://user-images.githubusercontent.com/15170090/195638551-dfced7be-c18f-4611-b015-cfefab480cee.png" alt="Week calendar" width="250">

## Year view

The `YearCalendarView` class is a year-based calendar implementation. All topics covered above for the month calendar will apply to the year calendar. The year calendar also has additional xml attributes and class properties/methods, typically with a `year` prefix/suffix. 

For example: `yearHeaderResource`, `scrollToYear()`, `findFirstVisibleYear()` and many others, but you get the idea.

To show the year calendar in your layout, add the view:

```xml
<com.kizitonwose.calendar.view.YearCalendarView
    android:id="@+id/yearCalendarView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:cv_dayViewResource="@layout/calendar_day_layout" />
```

Then follow the setup instructions above to provide a day resource/binder etc as you would do for the month calendar.

A year calendar implementation from the sample app:

<img src="https://github.com/user-attachments/assets/b2fcae94-341c-4f35-a997-e8d95a23efb4" alt="Year calendar" width="500">

Remember that all the screenshots shown so far are just examples of what you can achieve with the library and you can absolutely build your calendar to look however you want.

**Made a cool calendar with this library? Share an image [here](https://github.com/kizitonwose/Calendar/issues/1).**

## FAQ

**Q**: How do I use this library in a Java project?

**A**: It works out of the box, however, the `MonthScrollListener`, `WeekScrollListener` and `YearScrollListener` are not interfaces but Kotlin functions. To set the listener in a Java project see [this](https://github.com/kizitonwose/Calendar/issues/74).

**Q**: How do I disable user scrolling on the calendar so I can only scroll programmatically?

**A**: See [this](https://github.com/kizitonwose/Calendar/issues/38#issuecomment-525786644).

## Migration

Please see [the migration guide](https://github.com/kizitonwose/calendar/blob/main/docs/MigrationGuide.md#view).

---

--- METRICS ---
- Files Extracted: 4
- Estimated Token Budget: ~19292 tokens
- Canonical Reference: https://codewiki.google/github.com/kizitonwose/Calendar
