learning_ai_clock/ios/ChronoMind/Views/Components/CountdownRing.swift
2026-02-27 21:15:30 -08:00

99 lines
3.2 KiB
Swift

// Countdown Ring
// Visual countdown ring (like Time Timer) neurodivergent-friendly
// Color transitions: green yellow orange red as time decreases
import SwiftUI
struct CountdownRing: View {
let progress: Double // 0.0 (full) 1.0 (empty)
let urgency: UrgencyLevel
let remainingSeconds: TimeInterval
let totalSeconds: TimeInterval
var size: CGFloat = 220
var lineWidth: CGFloat = 12
private var ringColor: Color {
// Color transition based on remaining time ratio
let ratio = 1.0 - progress // 1.0 = full time, 0.0 = no time
if ratio > 0.5 {
return CMColors.gentle // green
} else if ratio > 0.25 {
return CMColors.standard // yellow
} else if ratio > 0.1 {
return CMColors.important // orange
} else {
return CMColors.critical // red
}
}
private var glowColor: Color {
ringColor.opacity(0.3)
}
var body: some View {
ZStack {
// Background track
Circle()
.stroke(CMColors.border, lineWidth: lineWidth)
.frame(width: size, height: size)
// Progress arc (fills clockwise from 12 o'clock)
Circle()
.trim(from: 0, to: 1.0 - progress)
.stroke(
ringColor,
style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)
)
.frame(width: size, height: size)
.rotationEffect(.degrees(-90))
.shadow(color: glowColor, radius: 8)
.animation(.easeInOut(duration: 0.3), value: progress)
// Center content
VStack(spacing: CMSpacing.xs) {
Text(formatDuration(remainingSeconds))
.font(CMFonts.mono(size: size * 0.18, weight: .bold))
.foregroundStyle(CMColors.text)
.contentTransition(.numericText())
if totalSeconds > 0 {
Text(formatDurationCompact(totalSeconds))
.font(CMFonts.body(size: size * 0.06))
.foregroundStyle(CMColors.textMuted)
}
}
}
}
}
// MARK: - Mini Countdown Ring (for cards)
struct MiniCountdownRing: View {
let progress: Double
let urgency: UrgencyLevel
var size: CGFloat = 32
var lineWidth: CGFloat = 3
private var ringColor: Color {
CMColors.urgencyColor(urgency)
}
var body: some View {
ZStack {
Circle()
.stroke(CMColors.border, lineWidth: lineWidth)
.frame(width: size, height: size)
Circle()
.trim(from: 0, to: 1.0 - progress)
.stroke(
ringColor,
style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)
)
.frame(width: size, height: size)
.rotationEffect(.degrees(-90))
.animation(.easeInOut(duration: 0.3), value: progress)
}
}
}