InfoTableView.swift 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. // LoopFollow
  2. // InfoTableView.swift
  3. import SwiftUI
  4. struct InfoTableView: View {
  5. @ObservedObject var infoManager: InfoManager
  6. var timeZoneOverride: String?
  7. @ScaledMetric(relativeTo: .body) private var fontSize: CGFloat = 17
  8. @ScaledMetric(relativeTo: .body) private var rowHeight: CGFloat = 21
  9. var body: some View {
  10. List {
  11. if let tz = timeZoneOverride {
  12. row(name: "Time Zone", value: tz)
  13. }
  14. ForEach(infoManager.visibleRows) { item in
  15. row(name: item.name, value: item.value, valueColor: color(for: item))
  16. }
  17. }
  18. .listStyle(.plain)
  19. .environment(\.defaultMinListRowHeight, rowHeight)
  20. }
  21. /// Threshold-based color for a row's value, or nil to use the default color.
  22. private func color(for item: InfoData) -> Color? {
  23. guard let numericValue = item.numericValue,
  24. let type = InfoType(rawValue: item.id),
  25. let config = type.colorConfig
  26. else { return nil }
  27. return Storage.shared.infoDisplayItems.value.item(for: type)?
  28. .coloring.color(for: numericValue, direction: config.direction)
  29. }
  30. private func row(name: String, value: String, valueColor: Color? = nil) -> some View {
  31. // Show a placeholder for any field that has no value yet,
  32. // so the row reads as "no data" rather than appearing empty.
  33. let displayValue = value.isEmpty ? "—" : value
  34. return ViewThatFits(in: .horizontal) {
  35. // Preferred: compact single line (label — value)
  36. HStack {
  37. Text(name)
  38. Spacer()
  39. Text(displayValue)
  40. .foregroundStyle(valueColor ?? .primary)
  41. }
  42. // Fallback when the single line won't fit: label over value
  43. VStack(alignment: .leading, spacing: 0) {
  44. Text(name)
  45. Text(displayValue)
  46. .foregroundStyle(valueColor ?? .primary)
  47. .frame(maxWidth: .infinity, alignment: .trailing)
  48. }
  49. }
  50. .font(.system(size: fontSize))
  51. .lineLimit(1)
  52. .minimumScaleFactor(0.5)
  53. .frame(minHeight: rowHeight)
  54. .listRowInsets(EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8))
  55. }
  56. }