feat: add Material 3 theme and UI components (EventCard, FilterDropdown, LoadingIndicator)

This commit is contained in:
2026-07-08 17:52:04 +02:00
parent 5aa264c2a7
commit af85605dd4
4 changed files with 192 additions and 0 deletions
@@ -0,0 +1,81 @@
package ch.parano.myice.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import ch.parano.myice.models.Event
@Composable
fun EventCard(
event: Event,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val stripeColor = event.color?.let { parseHexColor(it) }
?: MaterialTheme.colorScheme.primary
Card(
onClick = onClick,
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
) {
Row(
modifier = Modifier.height(120.dp),
) {
Box(
modifier = Modifier
.width(4.dp)
.fillMaxHeight()
.background(stripeColor),
)
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxHeight(),
verticalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(4.dp),
) {
Text(
text = "${event.agegroup} - ${event.name}",
style = MaterialTheme.typography.titleMedium,
)
Text(
text = event.title,
style = MaterialTheme.typography.bodyMedium,
)
Text(
text = "Adversaire: ${event.opponent}",
style = MaterialTheme.typography.bodySmall,
)
Text(
text = "Lieu: ${event.place}",
style = MaterialTheme.typography.bodySmall,
)
Text(
text = "${event.start} - ${event.end}",
style = MaterialTheme.typography.bodySmall,
)
}
}
}
}
private fun parseHexColor(hex: String): Color {
val cleanHex = hex.removePrefix("#")
val value = cleanHex.toLong(16)
return Color(0xFF000000 or value)
}
@@ -0,0 +1,56 @@
package ch.parano.myice.ui.components
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FilterDropdown(
label: String,
selectedValue: String?,
options: List<String>,
onSelect: (String?) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it },
modifier = modifier.fillMaxWidth(),
) {
OutlinedTextField(
value = selectedValue ?: "",
onValueChange = {},
readOnly = true,
label = { Text(label) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded) },
modifier = Modifier.menuAnchor().fillMaxWidth(),
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
options.forEach { option ->
DropdownMenuItem(
text = { Text(option) },
onClick = {
onSelect(option)
expanded = false
},
)
}
}
}
}
@@ -0,0 +1,18 @@
package ch.parano.myice.ui.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@Composable
fun LoadingIndicator(modifier: Modifier = Modifier) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
CircularProgressIndicator()
}
}
@@ -0,0 +1,37 @@
package ch.parano.myice.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
private val Indigo = Color(0xFF3F51B5)
private val IndigoLight = Color(0xFF7986CB)
private val IndigoDark = Color(0xFF303F9F)
private val LightColors = lightColorScheme(
primary = Indigo,
onPrimary = Color.White,
primaryContainer = IndigoDark,
secondary = IndigoLight,
)
private val DarkColors = darkColorScheme(
primary = IndigoLight,
onPrimary = Color.Black,
primaryContainer = Indigo,
secondary = Indigo,
)
@Composable
fun MyIceTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
MaterialTheme(
colorScheme = if (darkTheme) DarkColors else LightColors,
content = content,
)
}