Files
mobile-ios/Rosetta/DesignSystem/Components/ButtonStyles.swift
2026-02-23 11:35:29 +05:00

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))
}
}