refactor: streamline drag-and-drop reordering logic in ManageColumnsDialog and ListsScreen for consistency

This commit is contained in:
2025-10-26 01:24:23 +02:00
parent c9b50e9dcb
commit eaa0985b01
2 changed files with 72 additions and 100 deletions
@@ -6,7 +6,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -40,8 +39,6 @@ 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.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material.icons.filled.List 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
@@ -2499,28 +2496,31 @@ fun ManageColumnsDialog(
Divider() Divider()
// Column list with drag-and-drop reordering // Column list with drag-and-drop reordering
val reorderState = // Local extension mirroring the working behavior used in ListsScreen
rememberReorderableLazyListState(onMove = { from, to -> fun <T> MutableList<T>.move(
if (reorderedFields.isEmpty()) return@rememberReorderableLazyListState from: Int,
val lastIndex = reorderedFields.lastIndex to: Int,
if (lastIndex < 0) return@rememberReorderableLazyListState ) {
if (from == to) return
val fromIndex = from.index.coerceIn(0, lastIndex) if (isEmpty()) return
// Clamp target index into valid range; drop-at-end maps to lastIndex if (from !in indices) return
val toIndex = to.index.coerceIn(0, lastIndex) val item = removeAt(from)
val insertIndex =
if (fromIndex in 0..lastIndex && toIndex in 0..lastIndex) { when {
if (fromIndex != toIndex && reorderedFields.isNotEmpty()) { to > size -> size
val item = reorderedFields.removeAt(fromIndex) to < 0 -> 0
val newLastIndex = reorderedFields.lastIndex else -> to
var insertIndex = toIndex
// Adjust target when moving forward due to index shift after removal
if (fromIndex < toIndex) insertIndex = (toIndex - 1).coerceIn(0, newLastIndex + 1)
insertIndex = insertIndex.coerceIn(0, newLastIndex + 1)
reorderedFields.add(insertIndex, item)
}
} }
}) add(insertIndex, item)
}
val reorderState =
rememberReorderableLazyListState(
onMove = { from, to ->
// Use the same semantics as on the Lists screen for consistency
reorderedFields.move(from.index, to.index)
},
)
LazyColumn( LazyColumn(
modifier = modifier =
@@ -2536,34 +2536,39 @@ fun ManageColumnsDialog(
items = reorderedFields, items = reorderedFields,
key = { _, field -> field.id }, key = { _, field -> field.id },
) { index, field -> ) { index, field ->
ReorderableItem(reorderState, key = field.id) { _ -> ReorderableItem(
Box(modifier = Modifier.animateItemPlacement()) { reorderState,
key = field.id,
) { _ ->
Box(
modifier = Modifier.animateItemPlacement(),
) {
ColumnItem( ColumnItem(
field = field, field = field,
onEdit = { fieldToEdit = field }, onEdit = { fieldToEdit = field },
onDelete = { fieldToDelete = field }, onDelete = { fieldToDelete = field },
onMoveUp = { onMoveUp = {
if (index > 0) { if (index > 0) {
val item = reorderedFields.removeAt(index) val item = reorderedFields.removeAt(index)
reorderedFields.add(index - 1, item) reorderedFields.add(index - 1, item)
} }
}, },
onMoveDown = { onMoveDown = {
if (index < reorderedFields.size - 1) { if (index < reorderedFields.size - 1) {
val item = reorderedFields.removeAt(index) val item = reorderedFields.removeAt(index)
reorderedFields.add(index + 1, item) reorderedFields.add(index + 1, item)
} }
}, },
canMoveUp = index > 0, canMoveUp = index > 0,
canMoveDown = index < reorderedFields.size - 1, canMoveDown = index < reorderedFields.size - 1,
dragHandle = { dragHandle = {
Icon( Icon(
imageVector = Icons.Default.DragHandle, imageVector = Icons.Default.DragHandle,
contentDescription = "Reorder", contentDescription = "Reorder",
modifier = Modifier.detectReorder(reorderState), modifier = Modifier.detectReorder(reorderState),
tint = MaterialTheme.colorScheme.onSurfaceVariant, tint = MaterialTheme.colorScheme.onSurfaceVariant,
) )
}, },
) )
} }
} }
@@ -2661,8 +2666,9 @@ fun ColumnItem(
dragHandle: (@Composable () -> Unit)? = null, dragHandle: (@Composable () -> Unit)? = null,
) { ) {
Card( Card(
modifier = Modifier modifier =
.fillMaxWidth(), Modifier
.fillMaxWidth(),
colors = colors =
CardDefaults.cardColors( CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant, containerColor = MaterialTheme.colorScheme.surfaceVariant,
@@ -2682,40 +2688,6 @@ fun ColumnItem(
if (dragHandle != null) { if (dragHandle != null) {
dragHandle() dragHandle()
} }
Row {
IconButton(
onClick = onMoveUp,
enabled = canMoveUp,
modifier = Modifier.size(32.dp),
) {
Icon(
Icons.Default.KeyboardArrowUp,
contentDescription = "Move up",
tint =
if (canMoveUp) {
MaterialTheme.colorScheme.onSurfaceVariant
} else {
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f)
},
)
}
IconButton(
onClick = onMoveDown,
enabled = canMoveDown,
modifier = Modifier.size(32.dp),
) {
Icon(
Icons.Default.KeyboardArrowDown,
contentDescription = "Move down",
tint =
if (canMoveDown) {
MaterialTheme.colorScheme.onSurfaceVariant
} else {
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f)
},
)
}
}
} }
Spacer(modifier = Modifier.width(12.dp)) Spacer(modifier = Modifier.width(12.dp))
@@ -2,8 +2,8 @@
package com.collabtable.app.ui.screens package com.collabtable.app.ui.screens
import androidx.compose.foundation.clickable
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
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
@@ -201,18 +201,18 @@ fun ListsScreen(
ReorderableItem(reorderState, key = list.id) { _ -> ReorderableItem(reorderState, key = list.id) { _ ->
Box(modifier = Modifier.animateItemPlacement()) { Box(modifier = Modifier.animateItemPlacement()) {
ListItem( ListItem(
list = list, list = list,
onListClick = { onNavigateToList(list.id) }, onListClick = { onNavigateToList(list.id) },
onEditClick = { listToEdit = list }, onEditClick = { listToEdit = list },
onDeleteClick = { listToDelete = list }, onDeleteClick = { listToDelete = list },
dragHandle = { dragHandle = {
Icon( Icon(
imageVector = Icons.Default.DragHandle, imageVector = Icons.Default.DragHandle,
contentDescription = "Reorder", contentDescription = "Reorder",
modifier = Modifier.detectReorder(reorderState), modifier = Modifier.detectReorder(reorderState),
tint = MaterialTheme.colorScheme.onSurfaceVariant, tint = MaterialTheme.colorScheme.onSurfaceVariant,
) )
}, },
) )
} }
} }