The sort control leaves the list — three pickers settle into the bottom bar

Same segments, same persistence; the picker now rides each screen's
toolbar as a bottom-bar item shown only when there are rows to reorder,
instead of masquerading as the first row of the list it sorts.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-08-08 12:42:44 -04:00
parent e8791b239d
commit 49b2f4db6e
4 changed files with 60 additions and 30 deletions
+20 -10
View File
@@ -6,7 +6,7 @@ import SwiftUI
/// `session.snapshot` on every body evaluation, so a write this screen makes or one relayed
/// from elsewhere through the metadata query reaches the list the moment the session's reload
/// lands, and a lane deleted on another device is noticed rather than shown stale. A segmented
/// control above the card rows lets the list be sorted by name or by most recent change, on top
/// control in the toolbar lets the list be sorted by name or by most recent change, on top
/// of the lane's own manual arrangement, remembered across launches.
struct LaneScreen: View {
let boardRoot: URL
@@ -47,6 +47,9 @@ struct LaneScreen: View {
}
}
}
.toolbar {
sortToolbarItem
}
.task { session.open() }
.titlePromptAlert("New Card", isPresented: $isPresentingNewCard, placeholder: "Card Title") { title in
createCard(titled: title.isEmpty ? nil : title)
@@ -88,6 +91,22 @@ struct LaneScreen: View {
return title
}
/// Shown only once there is something to sort an empty or not-yet-loaded card list has no
/// rows for the segments to reorder.
@ToolbarContentBuilder
private var sortToolbarItem: some ToolbarContent {
if let lane, !lane.cards.isEmpty {
ToolbarItem(placement: .bottomBar) {
Picker("Sort", selection: sortOrder) {
Text("Manual").tag(ItemSortOrder.manual)
Text("Name").tag(ItemSortOrder.name)
Text("Recent").tag(ItemSortOrder.recent)
}
.pickerStyle(.segmented)
}
}
}
@ViewBuilder
private var content: some View {
if let lane {
@@ -106,15 +125,6 @@ struct LaneScreen: View {
description: Text("Add a card to this lane.")
)
} else {
Picker("Sort", selection: sortOrder) {
Text("Manual").tag(ItemSortOrder.manual)
Text("Name").tag(ItemSortOrder.name)
Text("Recent").tag(ItemSortOrder.recent)
}
.pickerStyle(.segmented)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
// The trusted order is `.manual`'s base `Lane.cards` is already in display
// order, exactly as the lane list trusts `BoardModel.lanes` and `.name`/
// `.recent` layer a display-only re-sort on top, never touching that base or