TIRGraphView.swift 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. // LoopFollow
  2. // TIRGraphView.swift
  3. import Charts
  4. import SwiftUI
  5. struct TIRGraphView: View {
  6. let tirData: [TIRDataPoint]
  7. private enum Band: String, CaseIterable, Plottable {
  8. case veryLow = "Very Low"
  9. case low = "Low"
  10. case inRange = "In Range"
  11. case high = "High"
  12. case veryHigh = "Very High"
  13. var color: Color {
  14. switch self {
  15. case .veryLow: return .red.opacity(0.8)
  16. case .low: return .red.opacity(0.5)
  17. case .inRange: return .green.opacity(0.7)
  18. case .high: return .yellow.opacity(0.7)
  19. case .veryHigh: return .orange.opacity(0.7)
  20. }
  21. }
  22. }
  23. private struct Slice: Identifiable {
  24. let period: TIRPeriod
  25. let band: Band
  26. let value: Double
  27. var id: String { "\(period.rawValue)-\(band.rawValue)" }
  28. }
  29. private var slices: [Slice] {
  30. tirData.flatMap { point in
  31. [
  32. Slice(period: point.period, band: .veryLow, value: point.veryLow),
  33. Slice(period: point.period, band: .low, value: point.low),
  34. Slice(period: point.period, band: .inRange, value: point.inRange),
  35. Slice(period: point.period, band: .high, value: point.high),
  36. Slice(period: point.period, band: .veryHigh, value: point.veryHigh),
  37. ]
  38. }
  39. }
  40. var body: some View {
  41. Chart(slices) { slice in
  42. BarMark(
  43. x: .value("period", slice.period.rawValue),
  44. y: .value("pct", slice.value),
  45. width: .ratio(0.6)
  46. )
  47. .foregroundStyle(by: .value("band", slice.band))
  48. }
  49. .chartForegroundStyleScale(domain: Band.allCases, range: Band.allCases.map(\.color))
  50. .chartLegend(.hidden)
  51. .chartYScale(domain: 0 ... 102)
  52. .chartYAxis {
  53. AxisMarks(position: .leading, values: [0, 25, 50, 75, 100]) { value in
  54. AxisGridLine(stroke: StrokeStyle(lineWidth: 0.5, dash: [5, 5]))
  55. AxisTick()
  56. AxisValueLabel {
  57. if let pct = value.as(Int.self) {
  58. Text("\(pct)%")
  59. }
  60. }
  61. }
  62. }
  63. .chartXAxis {
  64. AxisMarks(position: .bottom) { _ in
  65. AxisValueLabel()
  66. }
  67. }
  68. .allowsHitTesting(false)
  69. .background(Color(.systemBackground))
  70. }
  71. }