a8a9ad64f3
- Imports de CoreData que faltaban (MemberImportVisibility) en AccountsView, ImportDataView, ChartsContainerView y AddSourceView. - Aislamiento MainActor: IsolatedDefaultValues en el target de la app, LinearGradient @MainActor, WatchSyncMessage nonisolated, closures de ReviewPromptService/MonthlyCheckInStore/AppIntents. - Warnings de valores sin usar (SnapshotGapDetector, DashboardLayoutStore, OnboardingView, GoalEditorView, CoreDataStack, SettingsViewModel). - foregroundColor→foregroundStyle, cornerRadius→clipShape, navigationBarLeading/Trailing→topBarLeading/Trailing, Task.sleep(nanoseconds:)→sleep(for:), NavigationLink(isActive:)→ navigationDestination(isPresented:). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1u4K16xy7eQVtgsYNZ9Vn
31 lines
1.1 KiB
Swift
31 lines
1.1 KiB
Swift
import SwiftUI
|
|
|
|
/// Fitness-style progress ring: track + rounded-cap arc + centered percentage.
|
|
/// Color is semantic (pace/achievement), passed by the caller.
|
|
struct ProgressRing: View {
|
|
let progress: Double
|
|
var tint: Color = .appPrimary
|
|
var size: CGFloat = 56
|
|
var lineWidth: CGFloat = 6
|
|
|
|
private var clamped: Double { min(max(progress, 0), 1) }
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
Circle()
|
|
.stroke(tint.opacity(0.15), lineWidth: lineWidth)
|
|
Circle()
|
|
.trim(from: 0, to: clamped)
|
|
.stroke(tint, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))
|
|
.rotationEffect(.degrees(-90))
|
|
.animation(.easeOut(duration: 0.6), value: clamped)
|
|
Text("\(Int((clamped * 100).rounded()))%")
|
|
.font(.system(size: size * 0.26, weight: .bold, design: .rounded))
|
|
.foregroundStyle(tint)
|
|
.minimumScaleFactor(0.7)
|
|
}
|
|
.frame(width: size, height: size)
|
|
.accessibilityLabel(Text("\(Int((clamped * 100).rounded()))%"))
|
|
}
|
|
}
|