116 lines
3.5 KiB
Swift
116 lines
3.5 KiB
Swift
import SwiftUI
|
|
|
|
// MARK: - Primary Button (Liquid Glass)
|
|
|
|
struct RosettaPrimaryButtonStyle: ButtonStyle {
|
|
var isEnabled: Bool = true
|
|
|
|
func makeBody(configuration: Configuration) -> some View {
|
|
Group {
|
|
if #available(iOS 26, *) {
|
|
configuration.label
|
|
.background {
|
|
Capsule().fill(RosettaColors.primaryBlue.opacity(configuration.isPressed ? 0.7 : 1.0))
|
|
}
|
|
.glassEffect(.regular, in: Capsule())
|
|
} else {
|
|
configuration.label
|
|
.background { glassBackground(isPressed: configuration.isPressed) }
|
|
.clipShape(Capsule())
|
|
}
|
|
}
|
|
.opacity(isEnabled ? 1.0 : 0.5)
|
|
.scaleEffect(configuration.isPressed ? 0.97 : 1.0)
|
|
.animation(.easeOut(duration: 0.15), value: configuration.isPressed)
|
|
.allowsHitTesting(isEnabled)
|
|
}
|
|
|
|
private func glassBackground(isPressed: Bool) -> some View {
|
|
Capsule()
|
|
.fill(RosettaColors.primaryBlue.opacity(isPressed ? 0.7 : 1.0))
|
|
.overlay {
|
|
Capsule()
|
|
.fill(
|
|
LinearGradient(
|
|
colors: [
|
|
Color.white.opacity(0.18),
|
|
Color.clear,
|
|
Color.black.opacity(0.08),
|
|
],
|
|
startPoint: .top,
|
|
endPoint: .bottom
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Shimmer Overlay
|
|
|
|
struct ShimmerModifier: ViewModifier {
|
|
@State private var phase: CGFloat = -1.0
|
|
|
|
func body(content: Content) -> some View {
|
|
content
|
|
.overlay {
|
|
GeometryReader { geometry in
|
|
let width = geometry.size.width * 0.6
|
|
|
|
LinearGradient(
|
|
colors: [
|
|
Color.white.opacity(0),
|
|
Color.white.opacity(0.25),
|
|
Color.white.opacity(0),
|
|
],
|
|
startPoint: .leading,
|
|
endPoint: .trailing
|
|
)
|
|
.frame(width: width)
|
|
.offset(x: phase * (geometry.size.width + width))
|
|
.clipShape(Capsule())
|
|
}
|
|
}
|
|
.onAppear {
|
|
withAnimation(
|
|
.linear(duration: 2.0)
|
|
.repeatForever(autoreverses: false)
|
|
) {
|
|
phase = 1.0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
func shimmer() -> some View {
|
|
modifier(ShimmerModifier())
|
|
}
|
|
}
|
|
|
|
// MARK: - Stagger Animation Helper
|
|
|
|
struct StaggeredAppearance: ViewModifier {
|
|
let index: Int
|
|
let baseDelay: Double
|
|
let stagger: Double
|
|
@State private var isVisible = false
|
|
|
|
func body(content: Content) -> some View {
|
|
content
|
|
.opacity(isVisible ? 1.0 : 0.0)
|
|
.offset(y: isVisible ? 0 : 12)
|
|
.onAppear {
|
|
let delay = baseDelay + Double(index) * stagger
|
|
withAnimation(.easeOut(duration: 0.4).delay(delay)) {
|
|
isVisible = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
func staggeredAppearance(index: Int, baseDelay: Double = 0.2, stagger: Double = 0.05) -> some View {
|
|
modifier(StaggeredAppearance(index: index, baseDelay: baseDelay, stagger: stagger))
|
|
}
|
|
}
|