Files
OSGKeyboard/OSGKeyboardShared/DesignSystem/UsageStatCard.swift
T
rockyandClaude Opus 5 5d51c5bf77 chore: snapshot uncommitted local work before rebasing onto cloud-agent
Safety commit so the 94 uncommitted files in the working tree (in-flight
Account/Mac/Tip work plus the feature-preview recording tooling) survive the
move onto cursor/cloud-agent-1787824905651-vxuwc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 11:28:07 -07:00

117 lines
3.9 KiB
Swift

// UsageStatCard.swift
// OSGKeyboard · Shared
//
// Single cumulative metric tile. Compact = title / value / caption stack;
// prominent = horizontal hero bar for the primary word-count metric.
import SwiftUI
public struct UsageStatCard: View {
@Environment(\.themePalette) private var palette
public let title: String
public let value: String
public let caption: String
public var systemImage: String?
public var accent: Bool
/// Hero metric: wide horizontal layout for the primary word count.
public var prominent: Bool
/// Stretch to the container's height (split layouts aligning a grid with a taller sibling).
public var expands: Bool
public init(
title: String,
value: String,
caption: String,
systemImage: String? = nil,
accent: Bool = false,
prominent: Bool = false,
expands: Bool = false
) {
self.title = title
self.value = value
self.caption = caption
self.systemImage = systemImage
self.accent = accent
self.prominent = prominent
self.expands = expands
}
public var body: some View {
UsageSurfaceCard(padding: Spacing.md) {
if prominent {
prominentBody
} else {
compactBody
}
}
}
private var compactBody: some View {
VStack(alignment: .leading, spacing: Spacing.xs) {
HStack {
Text(title.uppercased())
.font(TypeStyle.caption2)
.tracking(0.6)
.foregroundStyle(palette.textTertiary)
Spacer()
if let systemImage {
Image(systemName: systemImage)
.font(.system(size: 13, weight: .semibold))
.foregroundStyle(accent ? palette.accent : palette.textTertiary)
.symbolRenderingMode(.hierarchical)
}
}
Text(value)
.font(TypeStyle.title2)
.foregroundStyle(accent ? palette.accent : palette.textPrimary)
.lineLimit(1)
.minimumScaleFactor(0.7)
.contentTransition(.numericText())
.animation(Motion.soft, value: value)
Text(caption)
.font(TypeStyle.caption)
.foregroundStyle(palette.textSecondary)
}
.frame(
maxWidth: .infinity,
maxHeight: expands ? .infinity : nil,
alignment: .topLeading
)
}
/// Wide "hero bar": icon badge + title/caption left, big number right.
private var prominentBody: some View {
HStack(spacing: Spacing.md) {
if let systemImage {
ZStack {
Circle()
.fill(palette.accentMuted)
.frame(width: 44, height: 44)
Image(systemName: systemImage)
.font(.system(size: 17, weight: .semibold))
.foregroundStyle(palette.accent)
.symbolRenderingMode(.hierarchical)
}
}
VStack(alignment: .leading, spacing: 2) {
Text(title.uppercased())
.font(TypeStyle.caption2)
.tracking(0.6)
.foregroundStyle(palette.textTertiary)
Text(caption)
.font(TypeStyle.caption)
.foregroundStyle(palette.textSecondary)
}
Spacer(minLength: Spacing.md)
Text(value)
.font(.system(size: 34, weight: .bold))
.foregroundStyle(accent ? palette.accent : palette.textPrimary)
.lineLimit(1)
.minimumScaleFactor(0.6)
.contentTransition(.numericText())
.animation(Motion.soft, value: value)
}
}
}