feat: enhance ListDetailScreen with auto-resize functionality for field headers
This commit is contained in:
+126
-49
@@ -48,6 +48,7 @@ import androidx.compose.material.icons.filled.List
|
|||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
|
import androidx.compose.material.icons.filled.FitScreen
|
||||||
import androidx.compose.material.icons.filled.Star
|
import androidx.compose.material.icons.filled.Star
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
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.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.SpanStyle
|
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.buildAnnotatedString
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
@@ -108,6 +111,10 @@ fun ListDetailScreen(
|
|||||||
val database = remember { CollabTableDatabase.getDatabase(context) }
|
val database = remember { CollabTableDatabase.getDatabase(context) }
|
||||||
val viewModel = remember { ListDetailViewModel(database, listId, context) }
|
val viewModel = remember { ListDetailViewModel(database, listId, context) }
|
||||||
val prefs = remember { PreferencesManager.getInstance(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 list by viewModel.list.collectAsState()
|
||||||
val fields by viewModel.fields.collectAsState()
|
val fields by viewModel.fields.collectAsState()
|
||||||
@@ -150,52 +157,10 @@ fun ListDetailScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply filtering, sorting, and grouping
|
// Scaffold with top bar and FAB
|
||||||
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(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
SmallTopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.clickable { showRenameListDialog = true },
|
modifier = Modifier.clickable { showRenameListDialog = true },
|
||||||
@@ -223,11 +188,10 @@ fun ListDetailScreen(
|
|||||||
Icon(Icons.Default.Settings, contentDescription = "Manage Columns")
|
Icon(Icons.Default.Settings, contentDescription = "Manage Columns")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
colors =
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
TopAppBarDefaults.topAppBarColors(
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
@@ -238,6 +202,40 @@ fun ListDetailScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
) { padding ->
|
) { 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(" | ||||||