mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-10 12:05:53 +00:00
- Move all macOS-specific code from root to mac/ directory - Move app icons and assets to dedicated assets/ directory - Update GitHub workflows for new structure - Consolidate documentation files - Clean up root directory for better multi-platform organization
98 lines
2.9 KiB
Swift
98 lines
2.9 KiB
Swift
import SwiftUI
|
|
|
|
/// A shimmer loading effect view that maintains consistent height
|
|
struct ShimmerView: View {
|
|
@State private var isAnimating = false
|
|
|
|
let height: CGFloat
|
|
let cornerRadius: CGFloat
|
|
|
|
init(height: CGFloat = 20, cornerRadius: CGFloat = 4) {
|
|
self.height = height
|
|
self.cornerRadius = cornerRadius
|
|
}
|
|
|
|
var body: some View {
|
|
RoundedRectangle(cornerRadius: cornerRadius)
|
|
.fill(
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [
|
|
Color.gray.opacity(0.3),
|
|
Color.gray.opacity(0.15),
|
|
Color.gray.opacity(0.3)
|
|
]),
|
|
startPoint: .leading,
|
|
endPoint: .trailing
|
|
)
|
|
)
|
|
.frame(height: height)
|
|
.mask(
|
|
RoundedRectangle(cornerRadius: cornerRadius)
|
|
.fill(
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [
|
|
Color.black.opacity(0.3),
|
|
Color.black,
|
|
Color.black.opacity(0.3)
|
|
]),
|
|
startPoint: isAnimating ? .leading : .trailing,
|
|
endPoint: isAnimating ? .trailing : .leading
|
|
)
|
|
)
|
|
.animation(
|
|
Animation.linear(duration: 1.5)
|
|
.repeatForever(autoreverses: false),
|
|
value: isAnimating
|
|
)
|
|
)
|
|
.onAppear {
|
|
isAnimating = true
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A text-sized shimmer that matches the height of text
|
|
struct TextShimmer: View {
|
|
let text: String
|
|
let font: Font
|
|
|
|
@State private var textHeight: CGFloat = 20
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
// Hidden text to measure height
|
|
Text(text)
|
|
.font(font)
|
|
.opacity(0)
|
|
.background(
|
|
GeometryReader { geometry in
|
|
Color.clear
|
|
.preference(key: HeightPreferenceKey.self, value: geometry.size.height)
|
|
}
|
|
)
|
|
.onPreferenceChange(HeightPreferenceKey.self) { height in
|
|
textHeight = height
|
|
}
|
|
|
|
ShimmerView(height: textHeight, cornerRadius: 4)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct HeightPreferenceKey: PreferenceKey {
|
|
static let defaultValue: CGFloat = 0
|
|
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
value = nextValue()
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
VStack(spacing: 20) {
|
|
ShimmerView()
|
|
.frame(width: 200)
|
|
|
|
TextShimmer(text: "Loading...", font: .body)
|
|
.frame(width: 100)
|
|
}
|
|
.padding()
|
|
}
|