Sources/Orgstar/ColumnViewSheet.swift
62 lines · 2478 bytes
1import OrgCore
2import SwiftUI
3
4/// `org-columns`, read-only: one row per entry, one column per column-format field, with
5/// summaries. Clicking a row goes to its heading.
6struct ColumnViewSheet: View {
7 let view: ColumnView.View
8 let jump: (Int) -> Void
9 @Environment(\.dismiss) private var dismiss
10
11 var body: some View {
12 VStack(alignment: .leading, spacing: 0) {
13 HStack {
14 Text("Column View").font(.headline)
15 Spacer()
16 Button("Done") { dismiss() }.keyboardShortcut(.cancelAction)
17 }
18 .padding(10)
19 Divider()
20 ScrollView([.horizontal, .vertical]) {
21 Grid(alignment: .leading, horizontalSpacing: 14, verticalSpacing: 4) {
22 GridRow {
23 ForEach(view.specs.indices, id: \.self) { i in
24 Text(view.specs[i].title).bold().foregroundStyle(.secondary)
25 }
26 }
27 Divider()
28 ForEach(view.rows.indices, id: \.self) { r in
29 let row = view.rows[r]
30 GridRow {
31 ForEach(view.specs.indices, id: \.self) { i in
32 Text(cell(row, i))
33 .lineLimit(1)
34 .frame(maxWidth: view.specs[i].width.map { CGFloat($0) * 8.5 }, alignment: .leading)
35 }
36 }
37 .contentShape(Rectangle())
38 .accessibilityAddTraits(.isButton)
39 .accessibilityAction {
40 jump(row.start)
41 dismiss()
42 }
43 .onTapGesture {
44 jump(row.start)
45 dismiss()
46 }
47 }
48 }
49 .font(.system(.body, design: .monospaced))
50 .padding(10)
51 }
52 .defaultScrollAnchor(.topLeading)
53 }
54 .frame(minWidth: 640, minHeight: 360)
55 }
56
57 /// ITEM with the heading's stars, as org shows it.
58 private func cell(_ row: ColumnView.Row, _ i: Int) -> String {
59 guard view.specs[i].property == "ITEM" else { return row.values[i] }
60 return String(repeating: "*", count: row.level) + " " + row.values[i]
61 }
62}