feat: enhance ListDetailScreen with auto-resize functionality for field headers
This commit is contained in:
+123
-46
@@ -48,6 +48,7 @@ import androidx.compose.material.icons.filled.List
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.FitScreen
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -71,6 +72,8 @@ import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
@@ -108,6 +111,10 @@ fun ListDetailScreen(
|
||||
val database = remember { CollabTableDatabase.getDatabase(context) }
|
||||
val viewModel = remember { ListDetailViewModel(database, listId, context) }
|
||||
val prefs = remember { PreferencesManager.getInstance(context) }
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
val density = LocalDensity.current
|
||||
val headerTextStyle = MaterialTheme.typography.labelLarge
|
||||
val bodyTextStyle = MaterialTheme.typography.bodyMedium
|
||||
|
||||
val list by viewModel.list.collectAsState()
|
||||
val fields by viewModel.fields.collectAsState()
|
||||
@@ -150,52 +157,10 @@ fun ListDetailScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// Apply filtering, sorting, and grouping
|
||||
val processedItems =
|
||||
remember(stableItems, stableFields, filterField, filterValue, sortField, sortAscending, groupByField) {
|
||||
var result = stableItems
|
||||
|
||||
// Apply filter
|
||||
if (filterField != null && filterValue.isNotBlank()) {
|
||||
result =
|
||||
result.filter { itemWithValues ->
|
||||
val value = itemWithValues.values.find { it.fieldId == filterField!!.id }?.value ?: ""
|
||||
value.contains(filterValue, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply sort
|
||||
if (sortField != null) {
|
||||
result =
|
||||
result.sortedWith(
|
||||
compareBy { itemWithValues ->
|
||||
val value = itemWithValues.values.find { it.fieldId == sortField!!.id }?.value ?: ""
|
||||
if (sortAscending) value else value
|
||||
},
|
||||
)
|
||||
if (!sortAscending) {
|
||||
result = result.reversed()
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// Group items if groupByField is set
|
||||
val groupedItems =
|
||||
remember(processedItems, groupByField) {
|
||||
if (groupByField != null) {
|
||||
processedItems.groupBy { itemWithValues ->
|
||||
itemWithValues.values.find { it.fieldId == groupByField!!.id }?.value ?: "(Empty)"
|
||||
}
|
||||
} else {
|
||||
mapOf("" to processedItems)
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold with top bar and FAB
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
SmallTopAppBar(
|
||||
title = {
|
||||
Row(
|
||||
modifier = Modifier.clickable { showRenameListDialog = true },
|
||||
@@ -223,8 +188,7 @@ fun ListDetailScreen(
|
||||
Icon(Icons.Default.Settings, contentDescription = "Manage Columns")
|
||||
}
|
||||
},
|
||||
colors =
|
||||
TopAppBarDefaults.topAppBarColors(
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
@@ -238,6 +202,40 @@ fun ListDetailScreen(
|
||||
}
|
||||
},
|
||||
) { padding ->
|
||||
// Apply filtering, sorting, and grouping to items
|
||||
fun valueFor(item: ItemWithValues, field: Field?): String {
|
||||
if (field == null) return ""
|
||||
return item.values.find { it.fieldId == field.id }?.value ?: ""
|
||||
}
|
||||
|
||||
val filteredItems: List<ItemWithValues> =
|
||||
if (filterField != null && filterValue.isNotBlank()) {
|
||||
val needle = filterValue.lowercase(Locale.getDefault())
|
||||
stableItems.filter { item ->
|
||||
valueFor(item, filterField).lowercase(Locale.getDefault()).contains(needle)
|
||||
}
|
||||
} else {
|
||||
stableItems
|
||||
}
|
||||
|
||||
val sortedItems: List<ItemWithValues> =
|
||||
if (sortField != null) {
|
||||
val base = filteredItems.sortedWith(compareBy { item -> valueFor(item, sortField) })
|
||||
if (sortAscending) base else base.asReversed()
|
||||
} else {
|
||||
filteredItems
|
||||
}
|
||||
|
||||
val processedItems: List<ItemWithValues> = sortedItems
|
||||
|
||||
val groupedItems: Map<String, List<ItemWithValues>> =
|
||||
if (groupByField != null) {
|
||||
processedItems.groupBy { item ->
|
||||
valueFor(item, groupByField).ifBlank { "(Empty)" }
|
||||
}
|
||||
} else {
|
||||
mapOf(" | ||||