ui: make filter card collapsible with 2x2 grid layout

- Collapsed: compact row showing 'Filtres' + active filter count + expand arrow
- Expanded: 2x2 grid (Compte+Type, Age+Sous-groupe) halves vertical space
- Animated expand/collapse with AnimatedVisibility
- FilterDropdown now respects weight() modifier for row layout
This commit is contained in:
2026-07-08 18:20:40 +02:00
parent c4bf05d728
commit 765fe51720
2 changed files with 97 additions and 35 deletions
@@ -28,7 +28,7 @@ fun FilterDropdown(
ExposedDropdownMenuBox( ExposedDropdownMenuBox(
expanded = expanded, expanded = expanded,
onExpandedChange = { expanded = it }, onExpandedChange = { expanded = it },
modifier = modifier.fillMaxWidth(), modifier = modifier,
) { ) {
OutlinedTextField( OutlinedTextField(
value = selectedValue ?: "", value = selectedValue ?: "",
@@ -1,8 +1,13 @@
package ch.parano.myice.ui.screens package ch.parano.myice.ui.screens
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -11,6 +16,7 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -20,6 +26,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -81,46 +90,99 @@ fun ScheduleScreen(
Column( Column(
modifier = Modifier.fillMaxSize().padding(padding), modifier = Modifier.fillMaxSize().padding(padding),
) { ) {
var filtersExpanded by remember { mutableStateOf(false) }
val activeFilterCount = listOf(
scheduleState.selectedAccount,
scheduleState.selectedAgegroup,
scheduleState.selectedSubgroup,
).count { it != null } + if (scheduleState.typeFilter != TypeFilter.GAMES) 1 else 0
Card( Card(
modifier = Modifier.fillMaxWidth().padding(16.dp), modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp),
) { ) {
Column( Row(
modifier = Modifier.padding(16.dp), modifier = Modifier
verticalArrangement = Arrangement.spacedBy(8.dp), .fillMaxWidth()
.clickable { filtersExpanded = !filtersExpanded }
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
) { ) {
FilterDropdown( Text(
label = "Compte", text = "Filtres",
selectedValue = scheduleState.selectedAccount, style = MaterialTheme.typography.titleSmall,
options = scheduleState.accounts.map { it.name }, fontWeight = FontWeight.Medium,
onSelect = { it?.let { acc -> scheduleViewModel.setAccount(acc) } },
) )
FilterDropdown( if (activeFilterCount > 0) {
label = "Age", Spacer(Modifier.height(0.dp))
selectedValue = scheduleState.selectedAgegroup, Text(
options = scheduleState.agegroups, text = "($activeFilterCount actif${if (activeFilterCount > 1) "s" else ""})",
onSelect = { scheduleViewModel.setAgegroup(it) }, style = MaterialTheme.typography.bodySmall,
) color = MaterialTheme.colorScheme.primary,
FilterDropdown( modifier = Modifier.padding(start = 8.dp),
label = "Sous-groupe", )
selectedValue = scheduleState.selectedSubgroup, }
options = scheduleState.subgroups, Spacer(Modifier.weight(1f))
onSelect = { scheduleViewModel.setSubgroup(it) }, Text(
) text = if (filtersExpanded) "" else "",
FilterDropdown( style = MaterialTheme.typography.bodySmall,
label = "Type",
selectedValue = typeFilterLabel(scheduleState.typeFilter),
options = listOf("Tous", "Matchs", "Entrainements"),
onSelect = { label ->
val filter = when (label) {
"Tous" -> TypeFilter.ALL
"Matchs" -> TypeFilter.GAMES
"Entrainements" -> TypeFilter.PRACTICES
else -> TypeFilter.DEFAULT
}
scheduleViewModel.setTypeFilter(filter)
},
) )
} }
AnimatedVisibility(
visible = filtersExpanded,
enter = expandVertically(),
exit = shrinkVertically(),
) {
Column(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
FilterDropdown(
label = "Compte",
selectedValue = scheduleState.selectedAccount,
options = scheduleState.accounts.map { it.name },
onSelect = { it?.let { acc -> scheduleViewModel.setAccount(acc) } },
modifier = Modifier.weight(1f),
)
FilterDropdown(
label = "Type",
selectedValue = typeFilterLabel(scheduleState.typeFilter),
options = listOf("Tous", "Matchs", "Entrainements"),
onSelect = { label ->
val filter = when (label) {
"Tous" -> TypeFilter.ALL
"Matchs" -> TypeFilter.GAMES
"Entrainements" -> TypeFilter.PRACTICES
else -> TypeFilter.DEFAULT
}
scheduleViewModel.setTypeFilter(filter)
},
modifier = Modifier.weight(1f),
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
FilterDropdown(
label = "Age",
selectedValue = scheduleState.selectedAgegroup,
options = scheduleState.agegroups,
onSelect = { scheduleViewModel.setAgegroup(it) },
modifier = Modifier.weight(1f),
)
FilterDropdown(
label = "Sous-groupe",
selectedValue = scheduleState.selectedSubgroup,
options = scheduleState.subgroups,
onSelect = { scheduleViewModel.setSubgroup(it) },
modifier = Modifier.weight(1f),
)
}
}
}
} }
Box( Box(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),