Skip to content

Commit cd09225

Browse files
committed
Refactor: Improve Calendar UI, Navigation, and Introduce Year Picker
This commit introduces several enhancements to the PersianDatePicker library, focusing on UI improvements, navigation refinements, and the addition of a year picker functionality. **Key Changes:** - **UI Enhancements & Animations:** - Implemented `AnimatedContent` for transitions when switching between `Picker` and `Input` display modes in `PersianDatePicker`. - Added `AnimatedContent` for month day items in `MonthGrid` for smoother visual feedback. - Introduced `AnimatedVisibility` for the new `YearPicker` dropdown. - **Year Picker Implementation:** - Added a new `YearPicker` composable in `io.github.faridsolgi.view.internal.PersianCalender.kt`. - This allows users to select a year from a grid within a dropdown menu. - The `YearPicker` is integrated into `NavigationMonthAndYearSelection`. - Added `SelectionYearLabelTextFont` to `PersianDatePickerTokens` for styling the year picker. - **Navigation and State:** - The `NavigationMonthAndYearSelection` now receives `colors` to style the `YearPicker`. - Updated `PersianDatePickerPreview` to initialize `rememberPersianDatePickerState` with a `yearRange`. - **Code Organization & Styling:** - Removed `ExposedDropdownMenuBox` and `ExposedDropdownMenu` from the previous year selection implementation. - Replaced with a custom dropdown implementation using `Card` and `LazyVerticalGrid` for the `YearPicker`. - Added constants `RecommendedSizeForAccessibility`, `MaxCalendarRows`, and `YearsInRow` for better layout management in `PersianCalender.kt`. - Minor UI adjustments like adding spacers and using `ProvideTextStyle` for the year picker label. - **Build Configuration:** - Updated library version from `0.0.3` to `0.0.10` in `library/build.gradle.kts`. These changes aim to provide a more interactive and user-friendly experience for selecting dates, particularly with the new year picking capability.
1 parent ec21a23 commit cd09225

File tree

4 files changed

+105
-59
lines changed

4 files changed

+105
-59
lines changed

library/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ plugins {
1616
}
1717

1818
group = "io.github.faridsolgi"
19-
version = "0.0.3"
19+
version = "0.0.10"
2020

2121
kotlin {
2222
jvm()

library/src/commonMain/kotlin/io/github/faridsolgi/domain/model/PersianDatePickerTokens.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@ internal object PersianDatePickerTokens {
1717
@Composable
1818
get() = MaterialTheme.typography.bodyMedium
1919
val todayDateBorderWidth = 1.dp
20+
val SelectionYearLabelTextFont
21+
@Composable
22+
get() = MaterialTheme.typography.bodyLarge
2023
}

library/src/commonMain/kotlin/io/github/faridsolgi/view/PersianDatePicker.kt

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
package io.github.faridsolgi.view
22

33

4+
import androidx.compose.animation.AnimatedContent
5+
import androidx.compose.animation.AnimatedVisibility
6+
import androidx.compose.animation.core.tween
7+
import androidx.compose.animation.fadeIn
8+
import androidx.compose.animation.fadeOut
9+
import androidx.compose.animation.slideInHorizontally
10+
import androidx.compose.animation.slideInVertically
11+
import androidx.compose.animation.slideOutHorizontally
12+
import androidx.compose.animation.slideOutVertically
13+
import androidx.compose.animation.togetherWith
414
import androidx.compose.foundation.background
515
import androidx.compose.foundation.layout.Arrangement
616
import androidx.compose.foundation.layout.Column
@@ -10,6 +20,7 @@ import androidx.compose.foundation.layout.Spacer
1020
import androidx.compose.foundation.layout.fillMaxWidth
1121
import androidx.compose.foundation.layout.padding
1222
import androidx.compose.foundation.layout.sizeIn
23+
import androidx.compose.material3.DatePicker
1324
import androidx.compose.material3.ExperimentalMaterial3Api
1425
import androidx.compose.material3.HorizontalDivider
1526
import androidx.compose.material3.MaterialTheme
@@ -52,7 +63,7 @@ fun PersianDatePicker(
5263
showModeToggle: Boolean = true,
5364
colors: PersianDatePickerColors = PersianDatePickerDefaults.colors(),
5465
) {
55-
// DatePicker()
66+
//DatePicker()
5667
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
5768
Column(modifier) {
5869
PersianDatePickerHeadLine(
@@ -78,19 +89,26 @@ fun SwitchablePersianDatePickerContents(
7889
colors: PersianDatePickerColors,
7990
modifier: Modifier = Modifier,
8091
) {
81-
Column(modifier) {
82-
when (state.displayMode) {
83-
DisplayMode.Companion.Picker -> {
84-
PersianDatePickerCalender(state, colors)
85-
}
8692

87-
DisplayMode.Companion.Input -> {
93+
AnimatedContent(
94+
targetState = state.displayMode ,
95+
transitionSpec = {
96+
slideInVertically(animationSpec = tween(10000)) { height -> height } + fadeIn() togetherWith
97+
slideOutVertically(animationSpec = tween(1000)) { height -> -height } + fadeOut()
98+
},
99+
label = "display mode transition"
100+
) { displayMode ->
101+
Column(modifier) {
102+
when (displayMode) {
103+
DisplayMode.Companion.Picker -> {
104+
PersianDatePickerCalender(state, colors)
105+
}
88106

107+
DisplayMode.Companion.Input -> {
108+
Text("test input")
109+
}
89110
}
90-
91-
else -> ""
92111
}
93-
94112
}
95113
}
96114

@@ -100,7 +118,7 @@ fun SwitchablePersianDatePickerContents(
100118
@Preview
101119
private fun PersianDatePickerPreview() {
102120
MaterialTheme {
103-
val state = rememberPersianDatePickerState()
121+
val state = rememberPersianDatePickerState(yearRange = 1400..1500)
104122

105123
PersianDatePickerDialog(
106124
dismissButton = {
@@ -121,8 +139,6 @@ private fun PersianDatePickerPreview() {
121139
state = state
122140
)
123141
}
124-
Spacer(Modifier.padding(16.dp))
125-
Text(state.selectedDate?.toDateString() ?: "")
126142
}
127143
}
128144

library/src/commonMain/kotlin/io/github/faridsolgi/view/internal/PersianCalender.kt

Lines changed: 72 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.faridsolgi.view.internal
22

33
import androidx.compose.animation.AnimatedContent
4+
import androidx.compose.animation.AnimatedVisibility
45
import androidx.compose.animation.ExperimentalAnimationApi
56
import androidx.compose.animation.animateColorAsState
67
import androidx.compose.animation.fadeIn
@@ -13,25 +14,32 @@ import androidx.compose.foundation.border
1314
import androidx.compose.foundation.clickable
1415
import androidx.compose.foundation.layout.Arrangement
1516
import androidx.compose.foundation.layout.Box
17+
import androidx.compose.foundation.layout.Column
1618
import androidx.compose.foundation.layout.Row
1719
import androidx.compose.foundation.layout.Spacer
1820
import androidx.compose.foundation.layout.aspectRatio
1921
import androidx.compose.foundation.layout.fillMaxWidth
22+
import androidx.compose.foundation.layout.height
2023
import androidx.compose.foundation.layout.padding
24+
import androidx.compose.foundation.layout.width
2125
import androidx.compose.foundation.lazy.grid.GridCells
2226
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
2327
import androidx.compose.foundation.lazy.grid.items
2428
import androidx.compose.foundation.shape.CircleShape
2529
import androidx.compose.material.icons.Icons
2630
import androidx.compose.material.icons.filled.ArrowDropDown
31+
import androidx.compose.material.icons.filled.ArrowDropUp
2732
import androidx.compose.material.icons.outlined.ChevronLeft
2833
import androidx.compose.material.icons.outlined.ChevronRight
29-
import androidx.compose.material3.ExperimentalMaterial3Api
30-
import androidx.compose.material3.ExposedDropdownMenuBox
34+
import androidx.compose.material3.ButtonDefaults
35+
import androidx.compose.material3.Card
36+
import androidx.compose.material3.CardDefaults
3137
import androidx.compose.material3.Icon
3238
import androidx.compose.material3.IconButton
3339
import androidx.compose.material3.MaterialTheme
40+
import androidx.compose.material3.ProvideTextStyle
3441
import androidx.compose.material3.Text
42+
import androidx.compose.material3.TextButton
3543
import androidx.compose.runtime.Composable
3644
import androidx.compose.runtime.getValue
3745
import androidx.compose.runtime.mutableStateOf
@@ -85,6 +93,7 @@ internal fun PersianDatePickerCalender(
8593
NavigationMonthAndYearSelection(
8694
displayedDate,
8795
state,
96+
colors,
8897
navigateToPreviousMonth = { state.navigateToPreviousMonth() },
8998
navigateToNextMonth = { state.navigateToNextMonth() },
9099
)
@@ -114,6 +123,7 @@ internal fun MonthGrid(
114123
modifier = Modifier.fillMaxWidth(),
115124
verticalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterVertically),
116125
horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally)
126+
117127
) {
118128

119129
// day weeks name
@@ -211,14 +221,15 @@ private fun MonthDayItem(
211221
internal fun NavigationMonthAndYearSelection(
212222
displayedDate: PersianDateTime,
213223
state: PersianDatePickerState,
224+
colors: PersianDatePickerColors,
214225
navigateToPreviousMonth: () -> Unit,
215226
navigateToNextMonth: () -> Unit,
216227
) {
217228
Row(
218229
Modifier.fillMaxWidth().padding(start = 8.dp),
219230
verticalAlignment = Alignment.CenterVertically
220231
) {
221-
YearSelector(displayedDate, state) {
232+
YearPicker(displayedDate, state, colors) {
222233
state.initDisplayedDate = it
223234
}
224235
Spacer(Modifier.weight(1f))
@@ -244,61 +255,77 @@ internal fun NavigationMonthAndYearSelection(
244255
}
245256
}
246257

247-
@OptIn(ExperimentalMaterial3Api::class)
248258
@Composable
249-
private fun YearSelector(
259+
fun YearPicker(
250260
displayedDate: PersianDateTime,
251261
state: PersianDatePickerState,
262+
colors: PersianDatePickerColors,
252263
onYearSelect: (PersianDateTime) -> Unit,
253264
) {
254265
var expanded by remember { mutableStateOf(false) }
255266

256-
ExposedDropdownMenuBox(
257-
expanded = expanded,
258-
onExpandedChange = { expanded = !expanded }
259-
) {
260-
Row(
261-
modifier = Modifier
262-
.clickable { expanded = !expanded }
263-
.padding(8.dp),
264-
verticalAlignment = Alignment.CenterVertically
265-
) {
266-
Text(text = displayedDate.format {
267-
monthName()
268-
char(' ')
269-
year()
270-
})
271-
Icon(
272-
imageVector = Icons.Default.ArrowDropDown,
273-
contentDescription = "Dropdown"
274-
)
275-
}
276-
277-
ExposedDropdownMenu(
278-
expanded = expanded,
279-
onDismissRequest = { expanded = false }
280-
) {
281-
// Display years in a grid
282-
LazyVerticalGrid(
283-
columns = GridCells.Fixed(3), // 3 columns
267+
ProvideTextStyle(PersianDatePickerTokens.SelectionYearLabelTextFont) {
268+
Column {
269+
// The clickable button
270+
if (expanded) {
271+
Spacer(Modifier.padding(8.dp))
272+
}
273+
Row(
284274
modifier = Modifier
285-
.padding(8.dp),
286-
verticalArrangement = Arrangement.spacedBy(8.dp),
287-
horizontalArrangement = Arrangement.spacedBy(8.dp)
275+
.clickable { expanded = !expanded },
276+
verticalAlignment = Alignment.CenterVertically
288277
) {
289-
items(state.yearRange.toList()) { item ->
290-
Text(
291-
text = item.toString(),
292-
modifier = Modifier
293-
.clickable {
294-
onYearSelect(displayedDate.copy(item))
295-
expanded = false
278+
Text(
279+
text = displayedDate.format {
280+
monthName()
281+
char(' ')
282+
year()
283+
}
284+
)
285+
Spacer(modifier = Modifier.width(8.dp))
286+
Icon(
287+
imageVector = if (expanded) Icons.Default.ArrowDropUp else Icons.Default.ArrowDropDown,
288+
contentDescription = "Dropdown"
289+
)
290+
}
291+
292+
// The custom dropdown
293+
AnimatedVisibility(expanded) {
294+
295+
Card(
296+
modifier = Modifier
297+
.height(PersianDatePickerTokens.ContainerHeight - 16.dp),
298+
elevation = CardDefaults.cardElevation(0.dp),
299+
300+
colors = CardDefaults.cardColors(containerColor = colors.containerColor)
301+
) {
302+
Box(modifier = Modifier.padding(8.dp)) {
303+
LazyVerticalGrid(
304+
columns = GridCells.Fixed(YearsInRow),
305+
verticalArrangement = Arrangement.spacedBy(8.dp),
306+
horizontalArrangement = Arrangement.spacedBy(8.dp),
307+
) {
308+
items(state.yearRange.toList()) { year ->
309+
TextButton(
310+
onClick = {
311+
onYearSelect(displayedDate.copy(year))
312+
expanded = false
313+
},
314+
colors = ButtonDefaults.textButtonColors(contentColor = colors.notSelectedDayColor)
315+
) {
316+
Text(year.toString())
317+
}
296318
}
297-
.padding(8.dp)
298-
)
319+
}
320+
}
299321
}
300322
}
301323
}
302324
}
303325
}
304326

327+
328+
internal val RecommendedSizeForAccessibility = 48.dp
329+
330+
private const val MaxCalendarRows = 6
331+
private const val YearsInRow: Int = 3

0 commit comments

Comments
 (0)