Files
wehub-resource-sync 869b84f27c
Build / build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

72 lines
2.6 KiB
Swift

import SwiftUI
struct EmptyStateView: View {
let title: LocalizedStringKey
let systemImage: String
let description: LocalizedStringKey
var action: (() -> Void)?
var actionLabel: LocalizedStringKey?
/// Halo/icon tint — positive states pass a color (e.g. green for "All
/// Clean"); neutral states keep the secondary look.
var tint: Color?
@State private var floating = false
@State private var popped = false
@Environment(\.accessibilityReduceMotion) private var reduceMotion
init(_ title: LocalizedStringKey, systemImage: String, description: LocalizedStringKey,
action: (() -> Void)? = nil, actionLabel: LocalizedStringKey? = nil, tint: Color? = nil) {
self.title = title
self.systemImage = systemImage
self.description = description
self.action = action
self.actionLabel = actionLabel
self.tint = tint
}
var body: some View {
VStack(spacing: 12) {
Spacer()
ZStack {
Circle()
.fill((tint ?? Color.secondary).opacity(0.10))
.frame(width: 96, height: 96)
Image(systemName: systemImage)
.font(.system(size: 40))
.foregroundStyle(tint ?? Color.secondary)
}
// One-shot entrance pop, then a gentle idle float. Both skipped
// under Reduce Motion.
.scaleEffect(popped || reduceMotion ? 1 : 0.8)
.offset(y: reduceMotion ? 0 : (floating ? -5 : 5))
.onAppear {
guard !reduceMotion else { return }
withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) {
popped = true
}
withAnimation(.easeInOut(duration: 2.4).repeatForever(autoreverses: true)) {
floating = true
}
}
Text(title)
.font(.title3.bold())
.staggered(0, baseDelay: 0.08)
Text(description)
.font(.callout)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
.frame(maxWidth: 300)
.staggered(1, baseDelay: 0.08)
if let action, let label = actionLabel {
Button(action: action) { Text(label) }
.buttonStyle(.borderedProminent)
.padding(.top, 4)
.staggered(2, baseDelay: 0.08)
}
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}