Sources/OrgSwiftUI/OrgTableView.swift
57 lines · 2275 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 @Environment(\.orgMath) private var math
11
12 var body: some View {
13 ScrollView(.horizontal, showsIndicators: false) {
14 Grid(alignment: .topLeading, horizontalSpacing: 14, verticalSpacing: 7) {
15 ForEach(Array(table.rows.enumerated()), id: \.element.id) { index, row in
16 GridRow {
17 ForEach(0 ..< table.columnCount, id: \.self) { column in
18 cell(row, column: column, isHeader: index < table.headerRowCount)
19 }
20 }
21 // The rule row separating the header band from the body.
22 if index + 1 == table.headerRowCount {
23 Divider().gridCellColumns(table.columnCount)
24 }
25 }
26 }
27 .padding(.vertical, 2)
28 }
29 }
30
31 @ViewBuilder
32 private func cell(_ row: OrgTableLayout.Row, column: Int, isHeader: Bool) -> some View {
33 // Org tolerates ragged rows; a missing cell is empty, not a crash.
34 let text = column < row.cells.count ? row.cells[column].text : AttributedString()
35 orgText(text, math: math)
36 .fontWeight(isHeader ? .semibold : .regular)
37 .multilineTextAlignment(Self.textAlignment(table.alignment(column: column)))
38 .fixedSize(horizontal: false, vertical: true)
39 .gridColumnAlignment(Self.gridAlignment(table.alignment(column: column)))
40 }
41
42 private static func gridAlignment(_ alignment: OrgAlignment?) -> HorizontalAlignment {
43 switch alignment {
44 case .center: .center
45 case .right: .trailing
46 case .left, nil: .leading
47 }
48 }
49
50 private static func textAlignment(_ alignment: OrgAlignment?) -> TextAlignment {
51 switch alignment {
52 case .center: .center
53 case .right: .trailing
54 case .left, nil: .leading
55 }
56 }
57}