Sources/OrgSwiftUI/OrgTableView.swift
56 lines · 2215 bytes
1import OrgSwift
2import SwiftUI
3
4/// A table, in a `Grid` that scrolls sideways when it is wider than the screen.
5///
6/// Column alignment comes from org's `|---+:---:+---|` separator row. `Grid` takes it from
7/// one cell per column, so it is set on the first row and inherited down.
8struct OrgTableView: View {
9 let table: OrgTableLayout
10
11 var body: some View {
12 ScrollView(.horizontal, showsIndicators: false) {
13 Grid(alignment: .topLeading, horizontalSpacing: 14, verticalSpacing: 7) {
14 ForEach(Array(table.rows.enumerated()), id: \.element.id) { index, row in
15 GridRow {
16 ForEach(0 ..< table.columnCount, id: \.self) { column in
17 cell(row, column: column, isHeader: index < table.headerRowCount)
18 }
19 }
20 // The rule row separating the header band from the body.
21 if index + 1 == table.headerRowCount {
22 Divider().gridCellColumns(table.columnCount)
23 }
24 }
25 }
26 .padding(.vertical, 2)
27 }
28 }
29
30 @ViewBuilder
31 private func cell(_ row: OrgTableLayout.Row, column: Int, isHeader: Bool) -> some View {
32 // Org tolerates ragged rows; a missing cell is empty, not a crash.
33 let text = column < row.cells.count ? row.cells[column].text : AttributedString()
34 Text(text)
35 .fontWeight(isHeader ? .semibold : .regular)
36 .multilineTextAlignment(Self.textAlignment(table.alignment(column: column)))
37 .fixedSize(horizontal: false, vertical: true)
38 .gridColumnAlignment(Self.gridAlignment(table.alignment(column: column)))
39 }
40
41 private static func gridAlignment(_ alignment: OrgAlignment?) -> HorizontalAlignment {
42 switch alignment {
43 case .center: .center
44 case .right: .trailing
45 case .left, nil: .leading
46 }
47 }
48
49 private static func textAlignment(_ alignment: OrgAlignment?) -> TextAlignment {
50 switch alignment {
51 case .center: .center
52 case .right: .trailing
53 case .left, nil: .leading
54 }
55 }
56}