import SwiftUI import Charts // MARK: - Point value labels for line charts // // Shared pieces for the "points + value labels" behavior across every line chart // in the Charts tab: permanent labels when the series is short (they carry a lot // of value at a glance), and a tap/drag selection bubble when it's dense. enum ChartLabels { /// A series with at most this many points shows its value labels permanently. static let alwaysShowThreshold = 12 static func compactCurrency(_ value: Decimal) -> String { value.compactCurrencyString } static func percent(_ value: Double, decimals: Int = 1) -> String { String(format: "%+.\(decimals)f%%", value) } } /// Small capsule bubble used both for permanent point labels and the selection popover. struct ChartValueBubble: View { let text: String var color: Color = .appPrimary var prominent: Bool = false var body: some View { Text(text) .font(prominent ? .caption.weight(.bold) : .caption2.weight(.semibold)) .foregroundColor(prominent ? .white : color) .padding(.horizontal, prominent ? 8 : 5) .padding(.vertical, prominent ? 4 : 2) .background( Capsule().fill(prominent ? color : color.opacity(0.12)) ) .fixedSize() } } /// Multi-line selection card (one row per series) used by multi-series charts. struct ChartSelectionCard: View { let title: String let rows: [(label: String, value: String, color: Color)] var body: some View { VStack(alignment: .leading, spacing: 3) { Text(title) .font(.caption2.weight(.semibold)) .foregroundColor(.secondary) ForEach(Array(rows.enumerated()), id: \.offset) { _, row in HStack(spacing: 5) { Circle().fill(row.color).frame(width: 6, height: 6) Text(row.label) .font(.caption2) .foregroundColor(.secondary) Text(row.value) .font(.caption2.weight(.semibold)) .foregroundColor(.primary) } } } .padding(8) .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 8)) .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray.opacity(0.2), lineWidth: 0.5)) .fixedSize() } }