Skip to content

Commit

Permalink
chore: merge branch 'release/1.9.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
ghasemdev committed Oct 12, 2023
2 parents 268d61b + fdcf854 commit 2a483b7
Show file tree
Hide file tree
Showing 717 changed files with 69,693 additions and 40,083 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[![Version](https://shields.io/badge/VERSION-1.5.2-blue?style=for-the-badge)](https://github.com/ghasemdev/affogato/releases/tag/1.5.2)
[![Kotlin](https://shields.io/badge/Kotlin-1.7.10-pink?style=for-the-badge)](https://kotlinlang.org/)
[![Version](https://shields.io/badge/VERSION-1.9.0-blue?style=for-the-badge)](https://github.com/ghasemdev/affogato/releases/tag/1.5.2)
[![Kotlin](https://shields.io/badge/Kotlin-1.9.10-pink?style=for-the-badge)](https://kotlinlang.org/)
[![API](https://shields.io/badge/Api-+21-green?style=for-the-badge)](https://www.android.com/)
[![License MIT](https://shields.io/badge/LICENSE-MIT-orange?style=for-the-badge)](https://opensource.org/licenses/MIT)

Expand Down
2 changes: 2 additions & 0 deletions affogato-datepicker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build
.gradle
54 changes: 54 additions & 0 deletions affogato-datepicker/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Configuration.MIN_SDK

plugins {
alias(libs.plugins.android.library)
id(libs.plugins.maven.publish.get().pluginId)
}

apply {
from("$rootDir/android-library-build.gradle")
}

android {
namespace = "com.parsuomash.affogato.datepicker"

defaultConfig {
minSdk = MIN_SDK
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
lint {
baseline = file("$rootDir/config/lint/lint-baseline.xml")
}
}

dependencies {
// Compose -------------------------------------------------------------------------------------
implementation(libs.compose.ui)
implementation(libs.compose.foundation)
implementation(libs.compose.material)
implementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.tooling)
// Date
api(libs.persianDate)
// Immutable Collection
api(libs.kotlinx.collections.immutable)
}

afterEvaluate {
publishing {
publications {
create<MavenPublication>("release") {
groupId = "com.parsuomash.affogato"
artifactId = "affogato-datepicker"
version = libs.versions.affogato.get()

from(components["release"])
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.parsuomash.affogato.datepicker

import androidx.annotation.FloatRange
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.parsuomash.affogato.datepicker.picker.ListItemPicker
import com.parsuomash.affogato.datepicker.utils.fadingEdge
import com.parsuomash.affogato.datepicker.utils.persianMonthNames
import kotlinx.collections.immutable.toPersistentList

@Composable
fun PersianDatePicker(
modifier: Modifier = Modifier,
persianDatePickerState: PersianDatePickerState = rememberPersianDatePickerState(),
textStyle: TextStyle = MaterialTheme.typography.body1,
@FloatRange(from = 1.0, to = 2.0) selectedTextScale: Float = 1f,
dividerColor: Color = MaterialTheme.colors.primary
) {
val days = remember(persianDatePickerState.maxDay) {
(1..persianDatePickerState.maxDay).toPersistentList()
}
val months = remember(persianDatePickerState.maxMonth) {
(1..persianDatePickerState.maxMonth).toPersistentList()
}
val years = remember(persianDatePickerState.maxYear, persianDatePickerState.minYear) {
(persianDatePickerState.minYear..persianDatePickerState.maxYear).toPersistentList()
}

Row(
modifier = modifier.fadingEdge(
Brush.verticalGradient(
0f to Color.Transparent,
0.2f to Color.Black,
0.8f to Color.Black,
1f to Color.Transparent
)
),
horizontalArrangement = Arrangement.Center
) {
ListItemPicker(
modifier = Modifier
.weight(1f)
.padding(horizontal = 1.dp),
list = days,
provideValue = { persianDatePickerState.selectedDay },
textStyle = textStyle,
selectedTextScale = selectedTextScale,
dividersColor = dividerColor,
onValueChange = {
persianDatePickerState.updateDate(persianDay = it)
}
)

ListItemPicker(
modifier = Modifier
.weight(1f)
.padding(horizontal = 1.dp),
list = months,
provideValue = { persianDatePickerState.selectedMonth },
textStyle = textStyle,
selectedTextScale = selectedTextScale,
dividersColor = dividerColor,
label = {
if (persianDatePickerState.isDisplayMonthNames) {
persianMonthNames.getOrElse(it - 1) { "" }
} else {
it.toString()
}
},
onValueChange = {
persianDatePickerState.updateDate(persianMonth = it)
}
)

ListItemPicker(
modifier = Modifier
.weight(1f)
.padding(horizontal = 1.dp),
list = years,
provideValue = { persianDatePickerState.selectedYear },
textStyle = textStyle,
selectedTextScale = selectedTextScale,
dividersColor = dividerColor,
onValueChange = {
persianDatePickerState.updateDate(persianYear = it)
}
)
}

LaunchedEffect(Unit) {
persianDatePickerState.initDate()
}
}

@Preview(showBackground = true)
@Composable
private fun PersianDatePickerPreview() {
PersianDatePicker(modifier = Modifier.fillMaxWidth())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
@file:Suppress("MemberVisibilityCanBePrivate", "MagicNumber", "unused")

package com.parsuomash.affogato.datepicker

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.parsuomash.affogato.datepicker.utils.PersianDateWrapper
import java.util.*
import saman.zamani.persiandate.PersianDate

@Composable
fun rememberPersianDatePickerState(
isDisplayMonthNames: Boolean = true,
date: PersianDateWrapper = PersianDateWrapper(),
selectedYear: Int = date.value.shYear,
selectedMonth: Int = date.value.shMonth,
selectedDay: Int = date.value.shDay,
minYear: Int = 1300,
maxYear: Int = date.value.shYear,
maxMonth: Int = 12,
maxDay: Int = 31
): PersianDatePickerState {
return rememberSaveable(saver = PersianDatePickerState.Saver) {
PersianDatePickerState(
isDisplayMonthNames = isDisplayMonthNames,
date = date,
selectedYear = selectedYear,
selectedMonth = selectedMonth,
selectedDay = selectedDay,
minYear = minYear,
maxYear = maxYear,
maxMonth = maxMonth,
maxDay = maxDay
)
}
}

@Stable
class PersianDatePickerState(
isDisplayMonthNames: Boolean = true,
date: PersianDateWrapper = PersianDateWrapper(),
selectedYear: Int = date.value.shYear,
selectedMonth: Int = date.value.shMonth,
selectedDay: Int = date.value.shDay,
minYear: Int = 1300,
maxYear: Int = date.value.shYear,
maxMonth: Int = 12,
maxDay: Int = 31
) {
var isDisplayMonthNames: Boolean by mutableStateOf(isDisplayMonthNames)
var date: PersianDateWrapper by mutableStateOf(date)

var minYear: Int by mutableIntStateOf(minYear)
var maxYear: Int by mutableIntStateOf(maxYear)
var maxMonth: Int by mutableIntStateOf(maxMonth)
var maxDay: Int by mutableIntStateOf(maxDay)

var selectedYear: Int by mutableIntStateOf(selectedYear)
var selectedMonth: Int by mutableIntStateOf(selectedMonth)
var selectedDay: Int by mutableIntStateOf(selectedDay)

val persianYear: Int get() = date.value.shYear
val persianMonth: Int get() = date.value.shMonth
val persianDay: Int get() = date.value.shDay

val gregorianDate: Date? get() = date.value.toDate()
val gregorianYear: Int get() = date.value.grgYear
val gregorianMonth: Int get() = date.value.grgMonth
val gregorianDay: Int get() = date.value.grgDay

val dayOfWeek: Int get() = date.value.dayOfWeek()
val persianMonthName: String? get() = date.value.monthName
val persianDayOfWeekName: String? get() = date.value.dayName()

val timestamp: Long get() = date.value.time

fun updateDate(timestamp: Long) {
date = PersianDateWrapper(value = PersianDate(timestamp))
}

fun updateDate(date: Date) {
this.date = PersianDateWrapper(value = PersianDate(date))
}

fun updateDate(persianYear: Int? = null, persianMonth: Int? = null, persianDay: Int? = null) {
if (persianYear != null) selectedYear = persianYear
if (persianMonth != null) selectedMonth = persianMonth
if (persianDay != null) selectedDay = persianDay

val isLeapYear = date.value.isLeap(selectedYear)

when {
selectedMonth < 7 -> {
maxDay = 31
}

selectedMonth < 12 -> {
if (selectedDay == 31) {
selectedDay = 30
}
maxDay = 30
}

selectedMonth == 12 -> {
if (isLeapYear) {
if (selectedDay == 31) {
selectedDay = 30
}
maxDay = 30
} else {
if (selectedDay > 29) {
selectedDay = 29
}
maxDay = 29
}
}
}

updateDate(
persianDay = selectedDay,
persianMonth = selectedMonth,
persianYear = selectedYear
)
}

private fun updateDate(persianYear: Int, persianMonth: Int, persianDay: Int) {
date.value.apply {
shYear = persianYear
shMonth = persianMonth
shDay = persianDay
}
}

fun resetDate(onDateChanged: ((year: Int, month: Int, day: Int) -> Unit)? = null) {
date = PersianDateWrapper()

selectedYear = date.value.shYear
selectedMonth = date.value.shMonth
selectedDay = date.value.shDay

if (onDateChanged != null) {
onDateChanged(
date.value.shYear,
date.value.shMonth,
date.value.shDay
)
}
}

fun initDate() {
if (minYear > selectedYear) {
minYear = selectedYear
}
if (maxYear < selectedYear) {
maxYear = selectedYear
}

if (selectedYear > maxYear) {
selectedYear = maxYear
}
if (selectedYear < minYear) {
selectedYear = minYear
}

if (selectedMonth in 7..11 && selectedDay == 31) {
selectedDay = 30
} else {
val isLeapYear = date.value.isLeap(selectedYear)
if (isLeapYear && selectedDay == 31) {
selectedDay = 30
} else if (selectedDay > 29) {
selectedDay = 29
}
}
}

override fun toString(): String = "$selectedYear/$selectedMonth/$selectedDay"

companion object {
/**
* The default [Saver] implementation for [PersianDatePickerState].
*/
val Saver: Saver<PersianDatePickerState, *> = listSaver(
save = {
listOf(
it.isDisplayMonthNames,
it.date.value.time,
it.selectedYear,
it.selectedMonth,
it.selectedDay,
it.minYear,
it.maxYear,
it.maxMonth,
it.maxDay
)
},
restore = {
PersianDatePickerState(
isDisplayMonthNames = it[0] as Boolean,
date = PersianDateWrapper(value = PersianDate(it[1] as Long)),
selectedYear = it[2] as Int,
selectedMonth = it[3] as Int,
selectedDay = it[4] as Int,
minYear = it[5] as Int,
maxYear = it[6] as Int,
maxMonth = it[7] as Int,
maxDay = it[8] as Int
)
}
)
}
}
Loading

0 comments on commit 2a483b7

Please sign in to comment.