ed737bc290
Home iPad/Mac (feedback del usuario: 'me gusta más el iPhone'): - Fuera el grid de 2 columnas (acoplaba alturas de fila y dejaba huecos entre cards de tamaños distintos) → columna única centrada a 720pt, patrón Things/Day One. Eliminado el drag-drop del grid, columnSpan y el overlay full-screen (~120 líneas); el orden/visibilidad sigue en Customize. Goals: - ProgressRing nuevo (anillo estilo Fitness con porcentaje centrado y color semántico: verde=logrado, ámbar=retrasado, acento=en camino) en GoalRowView y en la card de Goals del Home. GoalProgressBar queda solo en la share card. Journal: - Momentum & Streaks vive ahora en la cabecera del Journal (la historia del hábito es narrativa, no dashboard). - Filas timeline: círculo de mood como ancla visual (tint semántico por mood), mes + estrellas + preview de nota. Sources: - Badge 'Needs update' como chip ámbar visible; valor en tipografía rounded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
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))
|
|
.foregroundColor(tint)
|
|
.minimumScaleFactor(0.7)
|
|
}
|
|
.frame(width: size, height: size)
|
|
.accessibilityLabel(Text("\(Int((clamped * 100).rounded()))%"))
|
|
}
|
|
}
|