feat: update icon imports to use automirrored variants and refactor ListDetailScreen layout for improved scrolling behavior
This commit is contained in:
+41
-89
@@ -38,14 +38,14 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AccountBox
|
import androidx.compose.material.icons.filled.AccountBox
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.List
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.DateRange
|
import androidx.compose.material.icons.filled.DateRange
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.DragHandle
|
import androidx.compose.material.icons.filled.DragHandle
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
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
|
||||||
@@ -181,7 +181,7 @@ fun ListDetailScreen(
|
|||||||
},
|
},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onNavigateBack) {
|
IconButton(onClick = onNavigateBack) {
|
||||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
@@ -340,7 +340,7 @@ fun ListDetailScreen(
|
|||||||
},
|
},
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.List,
|
Icons.AutoMirrored.Filled.List,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(18.dp),
|
modifier = Modifier.size(18.dp),
|
||||||
)
|
)
|
||||||
@@ -491,8 +491,8 @@ fun ListDetailScreen(
|
|||||||
// If a new item was just added, scroll to bottom after composition
|
// If a new item was just added, scroll to bottom after composition
|
||||||
LaunchedEffect(processedItems.size, pendingScrollToBottom) {
|
LaunchedEffect(processedItems.size, pendingScrollToBottom) {
|
||||||
if (pendingScrollToBottom) {
|
if (pendingScrollToBottom) {
|
||||||
// totalItemsCount includes the trailing filler; scroll to the last real item index
|
// Scroll to the last real item index
|
||||||
val lastIndex = (listState.layoutInfo.totalItemsCount - 2).coerceAtLeast(0)
|
val lastIndex = (listState.layoutInfo.totalItemsCount - 1).coerceAtLeast(0)
|
||||||
try {
|
try {
|
||||||
listState.animateScrollToItem(lastIndex)
|
listState.animateScrollToItem(lastIndex)
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
@@ -501,87 +501,14 @@ fun ListDetailScreen(
|
|||||||
pendingScrollToBottom = false
|
pendingScrollToBottom = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
// Measure header height so we can allow content to scroll "behind" it
|
// Fixed header (does not participate in vertical scroll)
|
||||||
// but still make the last item fully visible by providing extra bottom padding.
|
|
||||||
var headerHeightPx by remember { mutableStateOf(0) }
|
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
state = listState,
|
|
||||||
contentPadding = PaddingValues(
|
|
||||||
top = 0.dp,
|
|
||||||
bottom = with(density) { headerHeightPx.toDp() },
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
groupedItems.entries.forEach { (groupName, groupItems) ->
|
|
||||||
// Show group header if grouping is enabled
|
|
||||||
if (groupByField != null) {
|
|
||||||
item(key = "group_$groupName") {
|
|
||||||
Surface(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
color = MaterialTheme.colorScheme.secondaryContainer,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "$groupName (${groupItems.size})",
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
modifier = Modifier.padding(12.dp),
|
|
||||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show items in the group
|
|
||||||
items(
|
|
||||||
items = groupItems,
|
|
||||||
key = { it.item.id },
|
|
||||||
) { itemWithValues ->
|
|
||||||
ItemRow(
|
|
||||||
fields = stableFields,
|
|
||||||
fieldWidths = fieldWidths,
|
|
||||||
itemWithValues = itemWithValues,
|
|
||||||
scrollState = horizontalScrollState,
|
|
||||||
onClick = { itemToEdit = itemWithValues },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filler area that occupies remaining viewport height and supports the same
|
|
||||||
// horizontal scroll physics (drag + fling) as the header/rows.
|
|
||||||
item(key = "hscroll_filler") {
|
|
||||||
val fillerScrollableState = rememberScrollableState { delta ->
|
|
||||||
// Mirror Row.horizontalScroll: rely on reverseDirection for LTR/RTL,
|
|
||||||
// and dispatch deltas directly to the shared ScrollState.
|
|
||||||
horizontalScrollState.dispatchRawDelta(delta)
|
|
||||||
}
|
|
||||||
Box(
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.fillParentMaxHeight()
|
|
||||||
.scrollable(
|
|
||||||
state = fillerScrollableState,
|
|
||||||
orientation = Orientation.Horizontal,
|
|
||||||
reverseDirection = LocalLayoutDirection.current == androidx.compose.ui.unit.LayoutDirection.Ltr,
|
|
||||||
flingBehavior = androidx.compose.foundation.gestures.ScrollableDefaults.flingBehavior(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overlaid header: content scrolls behind this until the end,
|
|
||||||
// thanks to the extra bottom padding applied above.
|
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.onGloballyPositioned { coordinates -> headerHeightPx = coordinates.size.height }
|
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.horizontalScroll(horizontalScrollState)
|
.horizontalScroll(horizontalScrollState),
|
||||||
.align(Alignment.TopStart)
|
|
||||||
.zIndex(1f),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
stableFields.forEach { field ->
|
stableFields.forEach { field ->
|
||||||
@@ -604,6 +531,31 @@ fun ListDetailScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Items list below the fixed header
|
||||||
|
val canScroll = listState.canScrollForward || listState.canScrollBackward
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
state = listState,
|
||||||
|
contentPadding = PaddingValues(0.dp),
|
||||||
|
userScrollEnabled = canScroll,
|
||||||
|
) {
|
||||||
|
groupedItems.entries.forEach { (_, groupItems) ->
|
||||||
|
// Show items in the group
|
||||||
|
items(
|
||||||
|
items = groupItems,
|
||||||
|
key = { it.item.id },
|
||||||
|
) { itemWithValues ->
|
||||||
|
ItemRow(
|
||||||
|
fields = stableFields,
|
||||||
|
fieldWidths = fieldWidths,
|
||||||
|
itemWithValues = itemWithValues,
|
||||||
|
scrollState = horizontalScrollState,
|
||||||
|
onClick = { itemToEdit = itemWithValues },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2718,7 +2670,7 @@ fun ManageColumnsDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
|
|
||||||
// Column list with drag-and-drop reordering
|
// Column list with drag-and-drop reordering
|
||||||
// Local extension mirroring the working behavior used in ListsScreen,
|
// Local extension mirroring the working behavior used in ListsScreen,
|
||||||
@@ -2790,7 +2742,7 @@ fun ManageColumnsDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
|
|
||||||
// Add button
|
// Add button
|
||||||
Row(
|
Row(
|
||||||
@@ -3027,7 +2979,7 @@ fun EditItemDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
|
|
||||||
// Fields list
|
// Fields list
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
@@ -3052,7 +3004,7 @@ fun EditItemDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
|
|
||||||
// Action buttons
|
// Action buttons
|
||||||
Row(
|
Row(
|
||||||
@@ -3262,7 +3214,7 @@ private fun FilterSortDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
|
|
||||||
// Group By Section
|
// Group By Section
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
@@ -3312,7 +3264,7 @@ private fun FilterSortDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
|
|
||||||
// Filter Section
|
// Filter Section
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
@@ -3374,7 +3326,7 @@ private fun FilterSortDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
|
|
||||||
// Action buttons
|
// Action buttons
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
Reference in New Issue
Block a user