oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1import SwiftUI
2
3struct SheetCell: View {
4 @Bindable var model: SheetModel
5 let point: Coordinate
6 @Binding var editing: Coordinate?
7 var select: () -> Void
8 var beginEditing: () -> Void = {}
9 var endEditing: () -> Void
10 var columnWidth: Double? = nil
11 var rowHeight: Double? = nil
12 @Environment(\.colorScheme) private var scheme
13 private func width(_ col: Int) -> Double { columnWidth ?? model.sizes.cols?[String(col)] ?? 110 }
14 private func height(_ row: Int) -> Double { rowHeight ?? model.sizes.rows?[String(row)] ?? 32 }
15 var body: some View {
16 let cell = model.cells[point]
17 let style = model.styles[cell?.f ?? ""]
18 let alignment: Alignment =
19 style?.a == "center"
20 ? .center : style?.a == "right" || (style?.a == nil && cell?.n == true) ? .trailing : .leading
21 return Group {
22 #if os(macOS)
23 if editing == point {
24 SheetCellEditor(text: $model.draft, enabled: model.mayWrite && !model.busy,
25 label: String(format: sheetsMacString("Edit %@"), point.ref),
26 commit: { commit(right: $0) }, cancel: {
27 guard !model.busy else { return }
28 model.discardAll(); endEditing()
29 })
30 .padding(.horizontal, 5)
31 .frame(width: width(point.c), height: height(point.r))
32 .background(fill(style))
33 .overlay { Rectangle().strokeBorder(Color.accentColor, lineWidth: 2) }
34 .accessibilityLabel(String(format: sheetsMacString("Edit %@"), point.ref))
35 } else { display(cell, style: style, alignment: alignment) }
36 #else
37 display(cell, style: style, alignment: alignment)
38 #endif
39 }
40 }
41 private func commit(right: Bool) {
42 guard !model.busy, model.selection.start == point else { return }
43 Task {
44 await model.commit(moveRight: right)
45 if !model.hasUnconfirmedChanges { endEditing() }
46 }
47 }
48 private func display(_ cell: Cell?, style: CellStyle?, alignment: Alignment) -> some View {
49 Button(action: select) {
50 Text(cell?.display ?? "").font(.system(size: 13, weight: style?.b == true ? .bold : .regular))
51 .italic(style?.i == true).lineLimit(1).padding(.horizontal, 5)
52 .frame(width: width(point.c), height: height(point.r), alignment: alignment)
53 .foregroundStyle(ink(style)).background(fill(style))
54 .contentShape(.rect)
55 .overlay {
56 Rectangle().strokeBorder(
57 model.selection.contains(point) ? Color.accentColor : Color.secondary.opacity(0.2),
58 lineWidth: model.selection.contains(point) ? 2 : 0.5)
59 }
60 }.buttonStyle(.plain)
61 .simultaneousGesture(TapGesture(count: 2).onEnded { if model.mayWrite, !model.busy, !model.hasUnconfirmedChanges { model.select(point); beginEditing() } })
62 .accessibilityLabel("\(point.ref), \(cell?.display ?? "Empty")")
63 .accessibilityValue(style?.lc == true ? "Imported colours have low contrast" : "")
64 .accessibilityAddTraits(model.selection.contains(point) ? .isSelected : [])
65 }
66 private func fill(_ style: CellStyle?) -> Color {
67 if let hex = style?.fx { return Color(hex: hex) }
68 if let h = style?.fh, let c = style?.fc { return .oklch(scheme == .dark ? 0.30 : 0.92, c, h) }
69 return Color.clear
70 }
71 private func ink(_ style: CellStyle?) -> Color {
72 if let hex = style?.k { return Color(hex: hex) }
73 if let h = style?.kh, let c = style?.kc { return .oklch(scheme == .dark ? 0.80 : 0.42, c, h) }
74 return .primary
75 }
76}
77