From 3033003297b150dfec7d114ca2b9a4423325d7c3 Mon Sep 17 00:00:00 2001 From: Jean-Philippe <112860634+jpbarbaud@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:36:30 +0100 Subject: [PATCH 1/3] update datePicker deprecatedColor --- .../compose/calendar/GrapesDatePicker.kt | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePicker.kt b/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePicker.kt index 42e598e9..f78bae70 100644 --- a/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePicker.kt +++ b/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePicker.kt @@ -9,6 +9,7 @@ import androidx.compose.material3.rememberDatePickerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import com.spendesk.grapes.compose.extensions.resetDateToMidnight import com.spendesk.grapes.compose.extensions.resetDateToTomorrowMidnight @@ -64,20 +65,20 @@ fun GrapesDatePicker( title = null, headline = null, colors = DatePickerDefaults.colors( - containerColor = GrapesTheme.colors.mainBackground, - titleContentColor = GrapesTheme.colors.mainNeutralDarkest, - headlineContentColor = GrapesTheme.colors.mainNeutralDarkest, - weekdayContentColor = GrapesTheme.colors.mainNeutralDarkest, - subheadContentColor = GrapesTheme.colors.mainNeutralDarkest, - yearContentColor = GrapesTheme.colors.mainNeutralDarkest, - currentYearContentColor = GrapesTheme.colors.mainNeutralDarkest, - selectedYearContentColor = GrapesTheme.colors.mainWhite, - selectedYearContainerColor = GrapesTheme.colors.mainPrimaryNormal, - dayContentColor = GrapesTheme.colors.mainNeutralDarkest, - selectedDayContentColor = GrapesTheme.colors.mainWhite, - selectedDayContainerColor = GrapesTheme.colors.mainPrimaryNormal, - todayContentColor = GrapesTheme.colors.mainPrimaryNormal, - todayDateBorderColor = GrapesTheme.colors.mainPrimaryNormal + containerColor = GrapesTheme.colors.structureBackground, + titleContentColor = GrapesTheme.colors.neutralDarker, + headlineContentColor = GrapesTheme.colors.neutralDarker, + weekdayContentColor = GrapesTheme.colors.neutralDarker, + subheadContentColor = GrapesTheme.colors.neutralDarker, + yearContentColor = GrapesTheme.colors.neutralDarker, + currentYearContentColor = GrapesTheme.colors.neutralDarker, + selectedYearContentColor = Color.White, + selectedYearContainerColor = GrapesTheme.colors.primaryNormal, + dayContentColor = GrapesTheme.colors.neutralDarker, + selectedDayContentColor = Color.White, + selectedDayContainerColor = GrapesTheme.colors.primaryNormal, + todayContentColor = GrapesTheme.colors.primaryNormal, + todayDateBorderColor = GrapesTheme.colors.primaryNormal ), ) From c7211112a4ec3b1384f4495fea6e3df3749f3dfa Mon Sep 17 00:00:00 2001 From: Jean-Philippe <112860634+jpbarbaud@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:15:45 +0100 Subject: [PATCH 2/3] Improve GrapesDatePicker with more parameters --- .../compose/calendar/GrapesDatePicker.kt | 166 ++++++++++-------- .../calendar/GrapesDatePickerDefaults.kt | 73 ++++++++ 2 files changed, 161 insertions(+), 78 deletions(-) create mode 100644 library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePickerDefaults.kt diff --git a/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePicker.kt b/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePicker.kt index f78bae70..9fbf0597 100644 --- a/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePicker.kt +++ b/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePicker.kt @@ -1,22 +1,23 @@ package com.spendesk.grapes.compose.calendar +import androidx.compose.foundation.layout.padding import androidx.compose.material3.DatePicker -import androidx.compose.material3.DatePickerDefaults +import androidx.compose.material3.DatePickerColors import androidx.compose.material3.DisplayMode import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SelectableDates +import androidx.compose.material3.Text import androidx.compose.material3.rememberDatePickerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.spendesk.grapes.compose.extensions.resetDateToMidnight -import com.spendesk.grapes.compose.extensions.resetDateToTomorrowMidnight import com.spendesk.grapes.compose.theme.GrapesTheme import java.util.Calendar import java.util.Date -import java.util.TimeZone /** * @author : dany @@ -27,116 +28,125 @@ import java.util.TimeZone * Grapes date picker which lets the user select a date via a calendar UI. * * @param modifier The [Modifier] to be applied to this date picker - * @param date The pre-selected date in the calendar. Defaults to now if not provided - * @param minDate The minimum selectable date in the calendar (previous dates will be disabled) if provided. Defaults to infinite in the past - * @param maxDate The maximum selectable date in the calendar (further dates will be disabled) if provided. Defaults to infinite in the future + * @param initialDisplayedDate The pre-selected date in the calendar. Defaults to now if not provided + * @param yearRange The range of years to be displayed in the year picker. Defaults to 1900-2100 + * @param dateEdges The minimum and maximum selectable dates in the calendar. Defaults to infinite in the past and future + * @param colors The colors to be used for the date picker + * @param title The title to be displayed at the top of the date picker + * @param headline The headline to be displayed below the title of the date picker * @param onDateSelected Callback when a date is selected in the picker */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun GrapesDatePicker( modifier: Modifier = Modifier, - date: Date? = null, - minDate: Date? = null, - maxDate: Date? = null, - onDateSelected: ((Date) -> Unit)? = null + initialDisplayedDate: Date? = null, + yearRange: IntRange = GrapesDatePickerDefaults.YearRange, + dateEdges: SelectableDates = GrapesDatePickerDefaults.selectableDatesEdges(), + colors: DatePickerColors = GrapesDatePickerDefaults.colors(), + title: (@Composable () -> Unit)? = null, + headline: (@Composable () -> Unit)? = null, + onDateSelected: ((Date) -> Unit)? = null, ) { val selectedDate = rememberDatePickerState( - initialSelectedDateMillis = date?.time, - initialDisplayedMonthMillis = date?.time, - yearRange = DatePickerDefaults.YearRange, + initialSelectedDateMillis = initialDisplayedDate?.time, + initialDisplayedMonthMillis = initialDisplayedDate?.time, + yearRange = yearRange, initialDisplayMode = DisplayMode.Picker, - selectableDates = object : SelectableDates { - override fun isSelectableDate(utcTimeMillis: Long): Boolean { - val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply { timeInMillis = utcTimeMillis } - - val isAfterMinDate = minDate?.let { calendar.time.after(it.resetDateToMidnight()) } ?: true - val isBeforeMaxDate = maxDate?.let { calendar.time.before(it.resetDateToTomorrowMidnight()) } ?: true - - return isAfterMinDate && isBeforeMaxDate - } - } + selectableDates = dateEdges, ) DatePicker( state = selectedDate, modifier = modifier, showModeToggle = false, - title = null, - headline = null, - colors = DatePickerDefaults.colors( - containerColor = GrapesTheme.colors.structureBackground, - titleContentColor = GrapesTheme.colors.neutralDarker, - headlineContentColor = GrapesTheme.colors.neutralDarker, - weekdayContentColor = GrapesTheme.colors.neutralDarker, - subheadContentColor = GrapesTheme.colors.neutralDarker, - yearContentColor = GrapesTheme.colors.neutralDarker, - currentYearContentColor = GrapesTheme.colors.neutralDarker, - selectedYearContentColor = Color.White, - selectedYearContainerColor = GrapesTheme.colors.primaryNormal, - dayContentColor = GrapesTheme.colors.neutralDarker, - selectedDayContentColor = Color.White, - selectedDayContainerColor = GrapesTheme.colors.primaryNormal, - todayContentColor = GrapesTheme.colors.primaryNormal, - todayDateBorderColor = GrapesTheme.colors.primaryNormal - ), + title = title, + headline = headline, + colors = colors, ) LaunchedEffect(selectedDate.selectedDateMillis) { selectedDate.selectedDateMillis?.let { - if (Date(it).resetDateToMidnight() != date?.resetDateToMidnight()) { + if (Date(it).resetDateToMidnight() != initialDisplayedDate?.resetDateToMidnight()) { onDateSelected?.invoke(Date(it)) } } } } -@Preview(showBackground = true) -@Composable -private fun GrapesDatePickerWithMinDateAndMaxDatePreview() { - val minDate = Calendar.getInstance().apply { add(Calendar.DAY_OF_WEEK, -1) }.time - val maxDate = Calendar.getInstance().apply { add(Calendar.DAY_OF_WEEK, 2) }.time +@OptIn(ExperimentalMaterial3Api::class) +private data class GrapesDatePickerData( + val title: String? = null, + val headline: String? = null, + val initialDisplayedDate: Date? = null, + val yearRange: IntRange? = null, + val dateEdges: SelectableDates? = null, + val onDateSelected: ((Date) -> Unit)? = null, +) - GrapesTheme { - GrapesDatePicker( - minDate = minDate, - maxDate = maxDate, - onDateSelected = { date -> println("Selected date: $date") } - ) - } -} +@OptIn(ExperimentalMaterial3Api::class) +private class GrapesDatePickerProvider : PreviewParameterProvider { -@Preview(showBackground = true) -@Composable -private fun GrapesDatePickerWithMinDatePreview() { - val minDate = Calendar.getInstance().apply { add(Calendar.DAY_OF_WEEK, -1) }.time + private val calendar = Calendar.getInstance() - GrapesTheme { - GrapesDatePicker( - minDate = minDate, - onDateSelected = { date -> println("Selected date: $date") } - ) - } -} + override val values: Sequence = sequenceOf( + GrapesDatePickerData( + title = "Select a date", + headline = "Choose a date to proceed", + ), -@Preview(showBackground = true) -@Composable -private fun GrapesDatePickerWithMaxDatePreview() { - val maxDate = Calendar.getInstance().apply { add(Calendar.DAY_OF_WEEK, 2) }.time + GrapesDatePickerData( + initialDisplayedDate = calendar.time, // Today + dateEdges = GrapesDatePickerDefaults.selectableDatesEdges( + minDate = calendar.apply { add(Calendar.DAY_OF_WEEK, -1) }.time, + maxDate = calendar.apply { add(Calendar.DAY_OF_WEEK, 2) }.time + ), + ), - GrapesTheme { - GrapesDatePicker( - maxDate = maxDate, - onDateSelected = { date -> println("Selected date: $date") } + GrapesDatePickerData( + dateEdges = GrapesDatePickerDefaults.selectableDatesEdges( + minDate = calendar.apply { add(Calendar.DAY_OF_WEEK, -1) }.time, + maxDate = null + ), + ), + + GrapesDatePickerData( + dateEdges = GrapesDatePickerDefaults.selectableDatesEdges( + minDate = null, + maxDate = calendar.apply { add(Calendar.DAY_OF_WEEK, 1) }.time + ), ) - } + ) } -@Preview(showBackground = true) +@OptIn(ExperimentalMaterial3Api::class) @Composable -private fun GrapesDatePickerPreview() { +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +private fun GrapesDatePickerPreview( + @PreviewParameter(GrapesDatePickerProvider::class) data: GrapesDatePickerData, +) { GrapesTheme { GrapesDatePicker( + title = data.title?.let { + @Composable { + Text( + modifier = Modifier.padding(GrapesTheme.dimensions.spacing3), + text = it, + style = GrapesTheme.typography.titleXl + ) + } + }, + headline = data.headline?.let { + @Composable { + Text( + modifier = Modifier.padding(GrapesTheme.dimensions.spacing3), + text = it, + style = GrapesTheme.typography.titleM + ) + } + }, + initialDisplayedDate = data.initialDisplayedDate, + dateEdges = data.dateEdges ?: GrapesDatePickerDefaults.selectableDatesEdges(), onDateSelected = { date -> println("Selected date: $date") } ) } diff --git a/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePickerDefaults.kt b/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePickerDefaults.kt new file mode 100644 index 00000000..43ad90ea --- /dev/null +++ b/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePickerDefaults.kt @@ -0,0 +1,73 @@ +package com.spendesk.grapes.compose.calendar + +import androidx.compose.material3.DatePickerColors +import androidx.compose.material3.DatePickerDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SelectableDates +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +import com.spendesk.grapes.compose.extensions.resetDateToMidnight +import com.spendesk.grapes.compose.extensions.resetDateToTomorrowMidnight +import com.spendesk.grapes.compose.theme.GrapesTheme +import java.util.Calendar +import java.util.Date +import java.util.TimeZone + +@Immutable +object GrapesDatePickerDefaults { + + val YearRange: IntRange = IntRange(1900, 2100) + + @ExperimentalMaterial3Api + @Composable + fun colors( + containerColor: Color = GrapesTheme.colors.structureBackground, + titleContentColor: Color = GrapesTheme.colors.neutralDarker, + headlineContentColor: Color = GrapesTheme.colors.neutralDarker, + weekdayContentColor: Color = GrapesTheme.colors.neutralDarker, + subheadContentColor: Color = GrapesTheme.colors.neutralDarker, + yearContentColor: Color = GrapesTheme.colors.neutralDarker, + currentYearContentColor: Color = GrapesTheme.colors.neutralDarker, + selectedYearContentColor: Color = Color.White, + selectedYearContainerColor: Color = GrapesTheme.colors.primaryNormal, + dayContentColor: Color = GrapesTheme.colors.neutralDarker, + selectedDayContentColor: Color = Color.White, + selectedDayContainerColor: Color = GrapesTheme.colors.primaryNormal, + todayContentColor: Color = GrapesTheme.colors.primaryNormal, + todayDateBorderColor: Color = GrapesTheme.colors.primaryNormal, + ): DatePickerColors = DatePickerDefaults.colors( + containerColor = containerColor, + titleContentColor = titleContentColor, + headlineContentColor = headlineContentColor, + weekdayContentColor = weekdayContentColor, + subheadContentColor = subheadContentColor, + yearContentColor = yearContentColor, + currentYearContentColor = currentYearContentColor, + selectedYearContentColor = selectedYearContentColor, + selectedYearContainerColor = selectedYearContainerColor, + dayContentColor = dayContentColor, + selectedDayContentColor = selectedDayContentColor, + selectedDayContainerColor = selectedDayContainerColor, + todayContentColor = todayContentColor, + todayDateBorderColor = todayDateBorderColor, + ) + + @OptIn(ExperimentalMaterial3Api::class) + fun selectableDatesEdges(minDate: Date? = null, maxDate: Date? = null): SelectableDates { + return if (minDate != null || maxDate != null) { + object : SelectableDates { + override fun isSelectableDate(utcTimeMillis: Long): Boolean { + val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply { timeInMillis = utcTimeMillis } + + val isAfterMinDate = minDate?.let { calendar.time.after(it.resetDateToMidnight()) } ?: true + val isBeforeMaxDate = maxDate?.let { calendar.time.before(it.resetDateToTomorrowMidnight()) } ?: true + + return isAfterMinDate && isBeforeMaxDate + } + } + } else { + object : SelectableDates {} + } + } +} From 84361d8e81461149e1179ec586112035758fcf1f Mon Sep 17 00:00:00 2001 From: Jean-Philippe <112860634+jpbarbaud@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:33:15 +0100 Subject: [PATCH 3/3] Create GrapesDatePickerDialog --- .../calendar/GrapesDatePickerDialog.kt | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePickerDialog.kt diff --git a/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePickerDialog.kt b/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePickerDialog.kt new file mode 100644 index 00000000..5b7cc8b6 --- /dev/null +++ b/library-compose/src/main/java/com/spendesk/grapes/compose/calendar/GrapesDatePickerDialog.kt @@ -0,0 +1,58 @@ +package com.spendesk.grapes.compose.calendar + +import androidx.compose.material3.DatePickerColors +import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.window.DialogProperties +import com.spendesk.grapes.compose.theme.GrapesTheme +import java.util.Date + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun GrapesDatePickerDialog( + initialDisplayedDate: Date, + onDateSelected: (selectedDate: Date) -> Unit, + modifier: Modifier = Modifier, + colors: DatePickerColors = GrapesDatePickerDefaults.colors(), + shape: Shape = GrapesTheme.shapes.shape2, + dismissOnBack: Boolean = true, + dismissOnClickOutside: Boolean = true, + confirmButton: @Composable () -> Unit = { }, + dismissButton: (@Composable () -> Unit)? = null, + onDismissRequest: (() -> Unit)? = null, +) { + DatePickerDialog( + modifier = modifier, + onDismissRequest = { onDismissRequest?.invoke() }, + confirmButton = confirmButton, + dismissButton = dismissButton, + colors = colors, + shape = shape, + properties = DialogProperties( + dismissOnBackPress = dismissOnBack, + dismissOnClickOutside = dismissOnClickOutside, + ), + content = { + GrapesDatePicker( + initialDisplayedDate = initialDisplayedDate, + onDateSelected = { date -> onDateSelected(date) } + ) + } + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Preview(showBackground = false) +@Composable +fun PreviewGrapesDatePickerDialog() { + GrapesTheme { + GrapesDatePickerDialog( + initialDisplayedDate = Date(), + onDateSelected = { } + ) + } +}