import SwiftUI struct MacroRing: View { let current: Double let goal: Double let color: Color let label: String let unit: String var size: CGFloat = 72 var lineWidth: CGFloat = 7 private var progress: Double { guard goal > 0 else { return 0 } return min(current / goal, 1.0) } private var remaining: Double { max(goal - current, 0) } var body: some View { VStack(spacing: 4) { ZStack { Circle() .stroke(color.opacity(0.12), lineWidth: lineWidth) Circle() .trim(from: 0, to: progress) .stroke( color, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round) ) .rotationEffect(.degrees(-90)) .animation(.easeOut(duration: 0.5), value: progress) VStack(spacing: 0) { Text("\(Int(remaining))") .font(.system(size: size * 0.22, weight: .bold, design: .rounded)) .foregroundStyle(Color.text1) Text("left") .font(.system(size: size * 0.13, weight: .medium)) .foregroundStyle(Color.text4) } } .frame(width: size, height: size) Text(label) .font(.caption2) .fontWeight(.medium) .foregroundStyle(Color.text3) } } } struct MacroRingLarge: View { let current: Double let goal: Double let color: Color var size: CGFloat = 120 var lineWidth: CGFloat = 10 private var progress: Double { guard goal > 0 else { return 0 } return min(current / goal, 1.0) } private var remaining: Double { max(goal - current, 0) } var body: some View { ZStack { Circle() .stroke(color.opacity(0.12), lineWidth: lineWidth) Circle() .trim(from: 0, to: progress) .stroke( color, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round) ) .rotationEffect(.degrees(-90)) .animation(.easeOut(duration: 0.5), value: progress) VStack(spacing: 2) { Text("\(Int(remaining))") .font(.system(size: size * 0.26, weight: .bold, design: .rounded)) .foregroundStyle(Color.text1) Text("remaining") .font(.system(size: size * 0.11, weight: .medium)) .foregroundStyle(Color.text4) } } .frame(width: size, height: size) } }