This commit is contained in:
2025-07-13 21:54:09 -04:00
parent 0545f5dbc7
commit bdaa406876
33 changed files with 984 additions and 714 deletions

View File

@ -0,0 +1,96 @@
import SwiftUI
import SwiftData
struct EntityListView<T: EditableEntity>: View {
@Environment(\.modelContext) private var modelContext
@Query var items: [T]
@State private var showingAddSheet = false
@State private var itemToEdit: T? = nil
@State private var itemToDelete: T? = nil
private var sortDescriptors: [SortDescriptor<T>]
init(sort: [SortDescriptor<T>] = [], searchString: String = "") {
self.sortDescriptors = sort
_items = Query(filter: #Predicate { item in
if searchString.isEmpty {
return true
} else {
return item.name.localizedStandardContains(searchString)
}
}, sort: sort)
}
@State private var isInsideNavigationStack: Bool = false
var body: some View {
let content = Form {
List {
ForEach(items) { item in
ListItem(title: item.name, count: item.count)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button(role: .destructive) {
itemToDelete = item
} label: {
Label("Delete", systemImage: "trash")
}
Button {
itemToEdit = item
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.indigo)
}
}
}
}
.navigationTitle(T.navigationTitle)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: { showingAddSheet.toggle() }) {
Image(systemName: "plus")
}
}
}
.sheet(isPresented: $showingAddSheet) {
T.formView(for: T.createNew())
}
.sheet(item: $itemToEdit) { item in
T.formView(for: item)
}
.confirmationDialog(
"Delete?",
isPresented: Binding<Bool>(
get: { itemToDelete != nil },
set: { if !$0 { itemToDelete = nil } }
),
titleVisibility: .visible
) {
Button("Delete", role: .destructive) {
if let item = itemToDelete {
modelContext.delete(item)
try? modelContext.save()
itemToDelete = nil
}
}
Button("Cancel", role: .cancel) {
itemToDelete = nil
}
} message: {
Text("Are you sure you want to delete \(itemToDelete?.name ?? "this item")?")
}
.background(
NavigationStackChecker(isInside: $isInsideNavigationStack)
)
if isInsideNavigationStack {
content
} else {
NavigationStack {
content
}
}
}
}