swiftui-navigation/Examples/Inventory/Inventory.swift

161 lines
4.0 KiB
Swift

import IdentifiedCollections
import SwiftUI
import SwiftUINavigation
class InventoryModel: ObservableObject {
@Published var inventory: IdentifiedArrayOf<ItemRowModel> {
didSet { self.bind() }
}
@Published var destination: Destination?
enum Destination: Equatable {
case add(Item)
case edit(Item)
}
init(
inventory: IdentifiedArrayOf<ItemRowModel> = [],
destination: Destination? = nil
) {
self.inventory = inventory
self.destination = destination
self.bind()
}
func delete(item: Item) {
withAnimation {
_ = self.inventory.remove(id: item.id)
}
}
func add(item: Item) {
withAnimation {
self.inventory.append(ItemRowModel(item: item))
self.destination = nil
}
}
func addButtonTapped() {
self.destination = .add(Item(color: nil, name: "", status: .inStock(quantity: 1)))
}
func cancelButtonTapped() {
self.destination = nil
}
func cancelEditButtonTapped() {
self.destination = nil
}
func commitEdit(item: Item) {
self.inventory[id: item.id]?.item = item
self.destination = nil
}
private func bind() {
for itemRowModel in self.inventory {
itemRowModel.onDelete = { [weak self, weak itemRowModel] in
guard let self, let itemRowModel else { return }
withAnimation {
self.delete(item: itemRowModel.item)
}
}
itemRowModel.onDuplicate = { [weak self] item in
guard let self else { return }
withAnimation {
self.add(item: item)
}
}
itemRowModel.onTap = { [weak self, weak itemRowModel] in
guard let self, let itemRowModel else { return }
self.destination = .edit(itemRowModel.item)
}
}
}
}
struct InventoryView: View {
@ObservedObject var model: InventoryModel
var body: some View {
List {
ForEach(
self.model.inventory,
content: ItemRowView.init(model:)
)
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("Add") { self.model.addButtonTapped() }
}
}
.navigationTitle("Inventory")
.navigationDestination(
unwrapping: self.$model.destination,
case: /InventoryModel.Destination.edit
) { $item in
ItemView(item: $item)
.navigationBarTitle("Edit")
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
self.model.cancelEditButtonTapped()
}
}
ToolbarItem(placement: .primaryAction) {
Button("Save") {
self.model.commitEdit(item: item)
}
}
}
}
.sheet(
unwrapping: self.$model.destination,
case: /InventoryModel.Destination.add
) { $itemToAdd in
NavigationStack {
ItemView(item: $itemToAdd)
.navigationTitle("Add")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") { self.model.cancelButtonTapped() }
}
ToolbarItem(placement: .primaryAction) {
Button("Save") { self.model.add(item: itemToAdd) }
}
}
}
}
}
}
struct InventoryView_Previews: PreviewProvider {
static var previews: some View {
let keyboard = Item(color: .blue, name: "Keyboard", status: .inStock(quantity: 100))
NavigationStack {
InventoryView(
model: InventoryModel(
inventory: [
ItemRowModel(
item: keyboard
),
ItemRowModel(
item: Item(color: .yellow, name: "Charger", status: .inStock(quantity: 20))
),
ItemRowModel(
item: Item(color: .green, name: "Phone", status: .outOfStock(isOnBackOrder: true))
),
ItemRowModel(
item: Item(
color: .green, name: "Headphones", status: .outOfStock(isOnBackOrder: false)
)
),
]
)
)
}
}
}