From a74643d5a605e853baa52d21558c03b70713d900 Mon Sep 17 00:00:00 2001 From: Karan Sharma <55722391+ksharma-xyz@users.noreply.github.com> Date: Fri, 29 Nov 2024 17:23:15 +1100 Subject: [PATCH] UI: Redesign DateTime selector and improve time picker layout (#395) ### TL;DR Improved the date/time selector UI with enhanced styling, layout adjustments, and reduced splash screen delay for development. ### What changed? - Reduced splash screen delay from 1200ms to 100ms for development purposes - Renamed `backgroundColor` parameter to `themeColor` in RadioButton component - Added proper theme content color handling in RadioButton - Enhanced DateTimeSelector screen with: - Reordered UI components (time picker above date selection) - Added selected date/time row with reset option - Adjusted time picker density for better visibility - Improved radio button layout and spacing - Added back navigation functionality ### How to test? 1. Navigate to the trip planner screen 2. Click on the "Leaving Now" text 3. Verify the date/time selector opens with: - Functioning radio buttons for Leave/Arrive options - Working time picker with proper theming - Visible date selection with navigation arrows - Back button navigation 4. Confirm the splash screen appears briefly during development ### Why make this change? To enhance the user experience by providing a more intuitive and visually appealing date/time selection interface while improving development workflow with faster splash screen transitions. --- .../xyz/ksharma/krail/splash/SplashScreen.kt | 2 +- .../planner/ui/components/DateTimeSelector.kt | 18 +-- .../DateTimeSelectorDestination.kt | 4 +- .../DateTimeSelectorScreen.kt | 150 +++++++++++------- .../planner/ui/timetable/TimeTableScreen.kt | 16 +- 5 files changed, 113 insertions(+), 77 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/xyz/ksharma/krail/splash/SplashScreen.kt b/composeApp/src/commonMain/kotlin/xyz/ksharma/krail/splash/SplashScreen.kt index e10082d7..810ddb6d 100644 --- a/composeApp/src/commonMain/kotlin/xyz/ksharma/krail/splash/SplashScreen.kt +++ b/composeApp/src/commonMain/kotlin/xyz/ksharma/krail/splash/SplashScreen.kt @@ -55,7 +55,7 @@ fun SplashScreen( val splashComplete by rememberUpdatedState(onSplashComplete) LaunchedEffect(key1 = Unit) { - delay(1200) + delay(100) // 1200 for Prod splashComplete() } } diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/DateTimeSelector.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/DateTimeSelector.kt index 303f0bfb..7d2e5f0c 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/DateTimeSelector.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/DateTimeSelector.kt @@ -59,16 +59,16 @@ fun DateTimeSelector( RadioButton( text = "Arrive", selected = true, - backgroundColor = themeColor, + themeColor = themeColor, ) RadioButton( text = "Leave", - backgroundColor = themeColor, + themeColor = themeColor, ) RadioButton( text = "Now", - backgroundColor = themeColor, + themeColor = themeColor, ) } @@ -183,7 +183,7 @@ fun IconButton( @Composable fun RadioButton( text: String, - backgroundColor: Color, + themeColor: Color, type: RadioButtonType = RadioButtonType.DEFAULT, modifier: Modifier = Modifier, selected: Boolean = false, @@ -199,12 +199,12 @@ fun RadioButton( ) .clip(shape = RoundedCornerShape(8.dp)) .background( - color = if (selected) backgroundColor else Color.Transparent, + color = if (selected) themeColor else Color.Transparent, shape = RoundedCornerShape(8.dp) ) .border( width = if (!selected) 1.dp else 0.dp, // Apply border only when not selected - color = backgroundColor, // Use backgroundColor for border color + color = themeColor, // Use backgroundColor for border color shape = RoundedCornerShape(8.dp) ) .padding(vertical = 4.dp, horizontal = 12.dp), @@ -213,7 +213,7 @@ fun RadioButton( Text( text = text, style = KrailTheme.typography.title, - color = if (selected) KrailTheme.colors.onSurface else backgroundColor, // themecontentcolor + color = if (selected) themeContentColor() else themeColor, ) } } @@ -241,7 +241,7 @@ private fun PreviewRadioButton() { RadioButton( text = "Hello", selected = true, - backgroundColor = KrailTheme.colors.trainTheme, + themeColor = KrailTheme.colors.trainTheme, onClick = {}) } } @@ -254,7 +254,7 @@ private fun PreviewRadioButtonUnselected() { RadioButton( text = "Hello", selected = false, - backgroundColor = KrailTheme.colors.trainTheme, + themeColor = KrailTheme.colors.trainTheme, onClick = {}) } } diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/datetimeselector/DateTimeSelectorDestination.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/datetimeselector/DateTimeSelectorDestination.kt index 9cf82f31..369d978d 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/datetimeselector/DateTimeSelectorDestination.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/datetimeselector/DateTimeSelectorDestination.kt @@ -10,6 +10,8 @@ internal fun NavGraphBuilder.dateTimeSelectorDestination(navController: NavHostC composable { backStackEntry -> val viewModel: DateTimeSelectorViewModel = koinViewModel() - DateTimeSelectorScreen() + DateTimeSelectorScreen(onBackClick = { + navController.popBackStack() + }) } } diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/datetimeselector/DateTimeSelectorScreen.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/datetimeselector/DateTimeSelectorScreen.kt index 11eb2117..895521b2 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/datetimeselector/DateTimeSelectorScreen.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/datetimeselector/DateTimeSelectorScreen.kt @@ -2,6 +2,7 @@ package xyz.ksharma.krail.trip.planner.ui.datetimeselector import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -14,21 +15,25 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.IconButton import androidx.compose.material3.TimePicker import androidx.compose.material3.TimePickerColors import androidx.compose.material3.TimePickerLayoutType import androidx.compose.material3.TimePickerState import androidx.compose.material3.rememberTimePickerState import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import krail.feature.trip_planner.ui.generated.resources.Res import krail.feature.trip_planner.ui.generated.resources.ic_chevron_left import krail.feature.trip_planner.ui.generated.resources.ic_chevron_right @@ -42,6 +47,7 @@ import xyz.ksharma.krail.taj.theme.getForegroundColor import xyz.ksharma.krail.trip.planner.ui.components.IconButton import xyz.ksharma.krail.trip.planner.ui.components.RadioButton import xyz.ksharma.krail.trip.planner.ui.components.hexToComposeColor +import xyz.ksharma.krail.trip.planner.ui.components.themeBackgroundColor import xyz.ksharma.krail.trip.planner.ui.timetable.ActionButton @OptIn(ExperimentalMaterial3Api::class) @@ -76,14 +82,9 @@ fun DateTimeSelectorScreen(modifier: Modifier = Modifier, onBackClick: () -> Uni LazyColumn(contentPadding = PaddingValues(vertical = 16.dp)) { item { - JourneyTimeOptionsRow(themeColor) - } - - item { - DateSelection( + JourneyTimeOptionsRow( themeColor = themeColor, - date = "Today, 12th July", - modifier = Modifier.padding(horizontal = 16.dp), + modifier = Modifier.padding(horizontal = 16.dp, vertical = 20.dp) ) } @@ -96,45 +97,78 @@ fun DateTimeSelectorScreen(modifier: Modifier = Modifier, onBackClick: () -> Uni ) TimeSelection( timePickerState = timePickerState, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 20.dp) + .align(Alignment.CenterHorizontally), + ) + } + + item { + DateSelection( + themeColor = themeColor, + date = "Today, 12th July", modifier = Modifier.padding(horizontal = 16.dp), ) } + item { + SelectedDateTimeRow() + } } } } +@Composable +private fun SelectedDateTimeRow(modifier: Modifier = Modifier, resetClick: () -> Unit = {}) { + Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(20.dp)) { + Text("Reset", modifier = Modifier.clickable { resetClick() }) + Text("Leaving at 12:00 PM") + } +} + @OptIn(ExperimentalMaterial3Api::class) @Composable fun TimeSelection( timePickerState: TimePickerState, - modifier: Modifier, + modifier: Modifier = Modifier, ) { val themeColorHex by LocalThemeColor.current val themeColor = themeColorHex.hexToComposeColor() val themeContentColor = getForegroundColor(themeColor) - TimePicker( - state = timePickerState, - colors = TimePickerColors( - containerColor = KrailTheme.colors.surface, - clockDialColor = KrailTheme.colors.surface, - selectorColor = themeColor, - periodSelectorBorderColor = themeColor, - clockDialSelectedContentColor = themeContentColor, - clockDialUnselectedContentColor = KrailTheme.colors.onSurface.copy(alpha = 0.6f), - periodSelectorSelectedContainerColor = themeColor, - periodSelectorUnselectedContainerColor = KrailTheme.colors.surface, - periodSelectorSelectedContentColor = themeContentColor, - periodSelectorUnselectedContentColor = KrailTheme.colors.onSurface.copy(alpha = 0.6f), - timeSelectorSelectedContainerColor = themeColor, - timeSelectorUnselectedContainerColor = KrailTheme.colors.surface, - timeSelectorSelectedContentColor = themeContentColor, - timeSelectorUnselectedContentColor = KrailTheme.colors.onSurface.copy(alpha = 0.6f) - ), - layoutType = TimePickerLayoutType.Vertical, - modifier = modifier.fillMaxWidth() - ) + Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) { + val density = LocalDensity.current + LaunchedEffect(density) { + println("Density: $density") + } + CompositionLocalProvider( + LocalDensity provides Density( + (density.density - 0.6f).coerceIn(1.5f, 3f), + fontScale = density.fontScale + ), + ) { + TimePicker( + state = timePickerState, + colors = TimePickerColors( + containerColor = themeBackgroundColor(), + clockDialColor = themeBackgroundColor(), + selectorColor = themeColor, + periodSelectorBorderColor = themeColor, + clockDialSelectedContentColor = themeContentColor, + clockDialUnselectedContentColor = KrailTheme.colors.onSurface.copy(alpha = 0.8f), + periodSelectorSelectedContainerColor = themeColor, + periodSelectorUnselectedContainerColor = KrailTheme.colors.surface, + periodSelectorSelectedContentColor = themeContentColor, + periodSelectorUnselectedContentColor = KrailTheme.colors.onSurface.copy(alpha = 0.6f), + timeSelectorSelectedContainerColor = themeColor, + timeSelectorUnselectedContainerColor = KrailTheme.colors.surface, + timeSelectorSelectedContentColor = themeContentColor, + timeSelectorUnselectedContentColor = KrailTheme.colors.onSurface.copy(alpha = 0.8f) + ), + layoutType = TimePickerLayoutType.Vertical, + modifier = Modifier.fillMaxWidth(), + ) + } + } } @Composable @@ -144,50 +178,44 @@ private fun DateSelection( modifier: Modifier = Modifier, onDateSelected: () -> Unit = {}, ) { - Column(modifier = modifier.fillMaxWidth().padding(vertical = 24.dp)) { + Row(modifier = modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + IconButton( + painter = painterResource(Res.drawable.ic_chevron_left), + color = themeColor, + modifier = Modifier.align(Alignment.CenterVertically) + ) Text( - text = "Select Date", - style = KrailTheme.typography.title, + text = date, + style = KrailTheme.typography.bodyLarge, color = KrailTheme.colors.onSurface, - modifier = Modifier.padding(vertical = 12.dp) + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + ) + IconButton( + painter = painterResource(Res.drawable.ic_chevron_right), + color = themeColor, + modifier = Modifier.align(Alignment.CenterVertically) ) - - Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { - IconButton( - painter = painterResource(Res.drawable.ic_chevron_left), - color = themeColor, - modifier = Modifier.align(Alignment.CenterVertically) - ) - Text( - text = date, - style = KrailTheme.typography.bodyLarge, - color = KrailTheme.colors.onSurface, - modifier = Modifier.weight(1f), - textAlign = TextAlign.Center, - ) - IconButton( - painter = painterResource(Res.drawable.ic_chevron_right), - color = themeColor, - modifier = Modifier.align(Alignment.CenterVertically) - ) - } } } @Composable -private fun JourneyTimeOptionsRow(themeColor: Color) { +private fun JourneyTimeOptionsRow(themeColor: Color, modifier: Modifier) { Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(16.dp) + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, ) { RadioButton( - text = "Arrive", + text = "Leave", + themeColor = themeColor, selected = true, - backgroundColor = themeColor, + modifier = Modifier.padding(start = 16.dp), ) RadioButton( - text = "Leave", - backgroundColor = themeColor, + text = "Arrive", + selected = false, + themeColor = themeColor, + modifier = Modifier.padding(start = 16.dp) ) } } diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt index 7b8434be..eec5bac0 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt @@ -3,6 +3,7 @@ package xyz.ksharma.krail.trip.planner.ui.timetable import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -150,10 +151,6 @@ fun TimeTableScreen( } } - item { - Spacer(modifier = Modifier.height(16.dp)) - } - item { Text( text = "Leaving Now", @@ -161,12 +158,21 @@ fun TimeTableScreen( color = themeColor, modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 16.dp).clickable { + .padding(horizontal = 16.dp, vertical = 12.dp) + .clickable( + role = Role.Button, + interactionSource = remember { MutableInteractionSource() }, + indication = null + ) { dateTimeSelectorClicked() }, ) } + item { + Spacer(modifier = Modifier.height(8.dp)) + } + if (timeTableState.isError) { item { ErrorMessage(