Files
boc/aamos-admin-upgrade/ios/AamosAdmin/Components/HealthRing.swift
T

129 lines
4.5 KiB
Swift
Raw Normal View History

import SwiftUI
struct HealthRing: View {
let progress: Double // 0.0 1.0
let color: Color
var lineWidth: CGFloat = 12
var size: CGFloat = 120
var label: String? = nil
@State private var animatedProgress: Double = 0
private var clampedProgress: Double { min(max(progress, 0), 1) }
private var percentText: String { "\(Int((animatedProgress * 100).rounded()))" }
var body: some View {
ZStack {
Circle()
.stroke(color.opacity(0.15), lineWidth: lineWidth)
Circle()
.trim(from: 0, to: animatedProgress)
.stroke(
color,
style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)
)
.rotationEffect(.degrees(-90))
.animation(
.spring(response: 0.85, dampingFraction: 0.72),
value: animatedProgress
)
VStack(spacing: 2) {
HStack(alignment: .firstTextBaseline, spacing: 1) {
Text(percentText)
.font(.system(size: size * 0.22, weight: .bold, design: .rounded))
.foregroundStyle(.primary)
.contentTransition(.numericText(countsDown: animatedProgress < clampedProgress))
.animation(
.spring(response: 0.85, dampingFraction: 0.72),
value: animatedProgress
)
Text("%")
.font(.system(size: size * 0.13, weight: .semibold, design: .rounded))
.foregroundStyle(.secondary)
}
if let label {
Text(label)
.font(.system(size: size * 0.1, weight: .medium))
.foregroundStyle(.secondary)
.lineLimit(1)
.minimumScaleFactor(0.5)
.frame(maxWidth: size * 0.55)
}
}
}
.frame(width: size, height: size)
.onAppear {
animatedProgress = clampedProgress
}
.onChange(of: progress) { _, newValue in
animatedProgress = min(max(newValue, 0), 1)
}
}
}
#Preview {
@Previewable @State var cpu: Double = 0.72
@Previewable @State var mem: Double = 0.45
@Previewable @State var disk: Double = 0.88
NavigationStack {
ScrollView {
LazyVStack(spacing: 32) {
HStack(spacing: 24) {
VStack(spacing: 8) {
HealthRing(
progress: cpu,
color: Color(.systemBlue),
label: "CPU"
)
Text("CPU")
.font(.caption.weight(.medium))
.foregroundStyle(.secondary)
}
VStack(spacing: 8) {
HealthRing(
progress: mem,
color: Color(.systemGreen),
label: "RAM"
)
Text("Memory")
.font(.caption.weight(.medium))
.foregroundStyle(.secondary)
}
VStack(spacing: 8) {
HealthRing(
progress: disk,
color: Color(.systemOrange),
label: "Disk"
)
Text("Disk")
.font(.caption.weight(.medium))
.foregroundStyle(.secondary)
}
}
HealthRing(
progress: 0.23,
color: Color(.systemRed),
lineWidth: 18,
size: 180,
label: "Error rate"
)
VStack(spacing: 8) {
Slider(value: $cpu, in: 0...1)
Text("CPU: \(Int(cpu * 100))%")
.font(.caption)
.foregroundStyle(.secondary)
}
.padding(.horizontal)
}
.padding()
}
.navigationTitle("Health")
.background(Color(.systemBackground))
}
}