feat: improve drag-and-drop functionality by interleaving dividers in ListsScreen and ManageColumnsDialog for better user experience
This commit is contained in:
+53
-48
@@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.ClickableText
|
import androidx.compose.foundation.text.ClickableText
|
||||||
@@ -2540,48 +2539,56 @@ fun ManageColumnsDialog(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.reorderable(reorderState),
|
.reorderable(reorderState),
|
||||||
state = reorderState.listState,
|
state = reorderState.listState,
|
||||||
contentPadding = PaddingValues(16.dp),
|
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||||
) {
|
) {
|
||||||
itemsIndexed(
|
// Interleave dividers as separate Lazy items so dividers don't move with dragged rows
|
||||||
items = reorderedFields,
|
items(
|
||||||
key = { _, field -> field.id },
|
count = reorderedFields.size * 2 - 1,
|
||||||
) { index, field ->
|
key = { pos ->
|
||||||
ReorderableItem(
|
if (pos % 2 == 0) reorderedFields[pos / 2].id else "divider-$pos"
|
||||||
reorderState,
|
},
|
||||||
key = field.id,
|
) { pos ->
|
||||||
) { _ ->
|
if (pos % 2 == 1) {
|
||||||
Box(
|
Divider()
|
||||||
modifier = Modifier.animateItemPlacement(),
|
} else {
|
||||||
) {
|
val index = pos / 2
|
||||||
if (index > 0) Divider()
|
val field = reorderedFields[index]
|
||||||
ColumnItem(
|
ReorderableItem(
|
||||||
field = field,
|
reorderState,
|
||||||
onEdit = { fieldToEdit = field },
|
key = field.id,
|
||||||
onDelete = { fieldToDelete = field },
|
) { _ ->
|
||||||
onMoveUp = {
|
Box(
|
||||||
if (index > 0) {
|
modifier = Modifier.animateItemPlacement(),
|
||||||
val item = reorderedFields.removeAt(index)
|
) {
|
||||||
reorderedFields.add(index - 1, item)
|
ColumnItem(
|
||||||
}
|
field = field,
|
||||||
},
|
onEdit = { fieldToEdit = field },
|
||||||
onMoveDown = {
|
onDelete = { fieldToDelete = field },
|
||||||
if (index < reorderedFields.size - 1) {
|
onMoveUp = {
|
||||||
val item = reorderedFields.removeAt(index)
|
if (index > 0) {
|
||||||
reorderedFields.add(index + 1, item)
|
val item = reorderedFields.removeAt(index)
|
||||||
}
|
reorderedFields.add(index - 1, item)
|
||||||
},
|
}
|
||||||
canMoveUp = index > 0,
|
},
|
||||||
canMoveDown = index < reorderedFields.size - 1,
|
onMoveDown = {
|
||||||
dragHandle = {
|
if (index < reorderedFields.size - 1) {
|
||||||
Icon(
|
val item = reorderedFields.removeAt(index)
|
||||||
imageVector = Icons.Default.DragHandle,
|
reorderedFields.add(index + 1, item)
|
||||||
contentDescription = "Reorder",
|
}
|
||||||
modifier = Modifier.detectReorder(reorderState),
|
},
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
canMoveUp = index > 0,
|
||||||
)
|
canMoveDown = index < reorderedFields.size - 1,
|
||||||
},
|
dragHandle = {
|
||||||
)
|
Icon(
|
||||||
|
imageVector = Icons.Default.DragHandle,
|
||||||
|
contentDescription = "Reorder",
|
||||||
|
modifier = Modifier.detectReorder(reorderState),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2681,18 +2688,16 @@ fun ColumnItem(
|
|||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
.padding(horizontal = 8.dp, vertical = 10.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
// Drag handle
|
// Drag handle
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
if (dragHandle != null) {
|
||||||
if (dragHandle != null) {
|
dragHandle()
|
||||||
dragHandle()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
+35
-24
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package com.collabtable.app.ui.screens
|
package com.collabtable.app.ui.screens
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.clickable
|
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
|
||||||
@@ -14,8 +13,9 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
@@ -194,27 +194,38 @@ fun ListsScreen(
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.reorderable(reorderState),
|
.reorderable(reorderState),
|
||||||
state = reorderState.listState,
|
state = reorderState.listState,
|
||||||
contentPadding = PaddingValues(16.dp),
|
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||||
) {
|
) {
|
||||||
itemsIndexed(working, key = { _, it -> it.id }) { index, list ->
|
// Interleave dividers as separate list items so they don't move with dragged rows
|
||||||
ReorderableItem(reorderState, key = list.id) { _ ->
|
items(
|
||||||
Box(modifier = Modifier.animateItemPlacement()) {
|
count = working.size * 2 - 1,
|
||||||
if (index > 0) Divider()
|
key = { pos ->
|
||||||
ListItem(
|
if (pos % 2 == 0) working[pos / 2].id else "divider-$pos"
|
||||||
list = list,
|
},
|
||||||
onListClick = { onNavigateToList(list.id) },
|
) { pos ->
|
||||||
onEditClick = { listToEdit = list },
|
if (pos % 2 == 1) {
|
||||||
onDeleteClick = { listToDelete = list },
|
Divider()
|
||||||
dragHandle = {
|
} else {
|
||||||
Icon(
|
val index = pos / 2
|
||||||
imageVector = Icons.Default.DragHandle,
|
val list = working[index]
|
||||||
contentDescription = "Reorder",
|
ReorderableItem(reorderState, key = list.id) { _ ->
|
||||||
modifier = Modifier.detectReorder(reorderState),
|
Box(modifier = Modifier.animateItemPlacement()) {
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
ListItem(
|
||||||
)
|
list = list,
|
||||||
},
|
onListClick = { onNavigateToList(list.id) },
|
||||||
)
|
onEditClick = { listToEdit = list },
|
||||||
|
onDeleteClick = { listToDelete = list },
|
||||||
|
dragHandle = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.DragHandle,
|
||||||
|
contentDescription = "Reorder",
|
||||||
|
modifier = Modifier.detectReorder(reorderState),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,14 +291,14 @@ fun ListItem(
|
|||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
// Drag handle on the far left
|
// Drag handle on the far left
|
||||||
if (dragHandle != null) {
|
if (dragHandle != null) {
|
||||||
dragHandle()
|
dragHandle()
|
||||||
Spacer(modifier = Modifier.padding(start = 8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Table name centered/left and clickable
|
// Table name centered/left and clickable
|
||||||
|
|||||||
Reference in New Issue
Block a user