| 1 | import SwiftUI |
| 2 | |
| 3 | struct SheetPanels: View { |
| 4 | @Bindable var model: SheetModel |
| 5 | let panel: String |
| 6 | var openImported: ((UInt64, String) -> Void)? = nil |
| 7 | @Environment(\.dismiss) private var dismiss |
| 8 | @State private var title = "" |
| 9 | @State private var width = 110 |
| 10 | @State private var height = 32 |
| 11 | @State private var mode = "auto" |
| 12 | var body: some View { |
| 13 | NavigationStack { |
| 14 | Form { |
| 15 | if !model.error.isEmpty { Text(model.error).foregroundStyle(.red) } |
| 16 | switch panel { |
| 17 | case "Rename": |
| 18 | TextField("Sheet title", text: $title) |
| 19 | Button("Rename") { |
| 20 | Task { |
| 21 | guard await model.command("/rename", ["title": title]) else { return } |
| 22 | await model.loadList() |
| 23 | if let sheet = model.sheets.first(where: { $0.ID == model.page?.Sheet.ID }) { |
| 24 | await model.open(sheet) |
| 25 | } |
| 26 | dismiss() |
| 27 | } |
| 28 | } |
| 29 | case "Sizes": |
| 30 | TextField("Column width", value: $width, format: .number) |
| 31 | Button("Set column width") { Task { await model.resize(axis: "col", size: width) } } |
| 32 | .disabled(!(16...2000).contains(width)) |
| 33 | TextField("Row height", value: $height, format: .number) |
| 34 | Button("Set row height") { Task { await model.resize(axis: "row", size: height) } } |
| 35 | .disabled(!(16...2000).contains(height)) |
| 36 | Text("Formatting and size changes do not have undo.").font(.caption) |
| 37 | case "History": |
| 38 | ForEach(model.history) { entry in |
| 39 | VStack(alignment: .leading) { |
| 40 | Text(entry.Ref).font(.headline) |
| 41 | Text(entry.Who).font(.caption) |
| 42 | Text("Before: " + entry.Old).textSelection(.enabled) |
| 43 | Text("After: " + entry.New).textSelection(.enabled) |
| 44 | if model.mayWrite { |
| 45 | Button("Restore before this edit") { |
| 46 | Task { |
| 47 | await model.command("/restore", ["event": String(entry.ID)]) |
| 48 | await model.loadHistory() |
| 49 | } |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | } |
| 54 | case "Data": |
| 55 | Picker("Sort column", selection: $model.view.sortCol) { |
| 56 | Text("None").tag(-1) |
| 57 | ForEach(0..<26, id: \.self) { Text(Coordinate(r: 0, c: $0).ref.dropLast()).tag($0) } |
| 58 | } |
| 59 | Toggle("Descending", isOn: $model.view.sortDesc) |
| 60 | Picker("Filter column", selection: $model.view.filterCol) { |
| 61 | Text("None").tag(-1) |
| 62 | ForEach(0..<26, id: \.self) { Text(Coordinate(r: 0, c: $0).ref.dropLast()).tag($0) } |
| 63 | } |
| 64 | Picker("Comparison", selection: $model.view.filterOp) { |
| 65 | ForEach(["contains", "equals", "gt", "lt", "nonempty"], id: \.self) { Text($0).tag($0) } |
| 66 | } |
| 67 | TextField("Filter value", text: $model.view.filterVal) |
| 68 | Button("Apply and open table") { |
| 69 | Task { |
| 70 | guard await model.saveView() else { return } |
| 71 | model.showTable = true |
| 72 | dismiss() |
| 73 | } |
| 74 | }.disabled(!model.mayWrite) |
| 75 | Button("Open table") { |
| 76 | model.showTable = true |
| 77 | dismiss() |
| 78 | } |
| 79 | Text("Sorting changes display order. Cell references stay in their original positions.") |
| 80 | .font(.caption) |
| 81 | case "Comments": SheetComments(model: model) |
| 82 | case "Share": SheetSharing(model: model) |
| 83 | case "Import", "Export": SheetFiles(model: model, importing: panel == "Import", openImported: openImported) |
| 84 | default: EmptyView() |
| 85 | } |
| 86 | }.formStyle(.grouped).navigationTitle(panel) |
| 87 | .toolbar { ToolbarItem(placement: .confirmationAction) { Button("Done") { dismiss() } } } |
| 88 | } |
| 89 | #if os(macOS) |
| 90 | .frame(minWidth: 480, minHeight: 500) |
| 91 | #endif |
| 92 | .task { |
| 93 | title = model.page?.Sheet.Title ?? "" |
| 94 | if panel == "History" { await model.loadHistory() } |
| 95 | if panel == "Data" { await model.loadTable() } |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |