// // ViewModifiers.swift // SportsTime // // Reusable view modifiers for consistent styling across the app. // import SwiftUI // MARK: - Card Style Modifier struct CardStyle: ViewModifier { @Environment(\.colorScheme) private var colorScheme var cornerRadius: CGFloat = Theme.CornerRadius.large var padding: CGFloat = Theme.Spacing.lg func body(content: Content) -> some View { content .padding(padding) .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: cornerRadius)) .overlay { RoundedRectangle(cornerRadius: cornerRadius) .stroke(Theme.surfaceGlow(colorScheme), lineWidth: 1) } .shadow(color: Theme.cardShadow(colorScheme), radius: 10, y: 5) } } extension View { func cardStyle(cornerRadius: CGFloat = Theme.CornerRadius.large, padding: CGFloat = Theme.Spacing.lg) -> some View { modifier(CardStyle(cornerRadius: cornerRadius, padding: padding)) } } // MARK: - Glow Effect Modifier struct GlowEffect: ViewModifier { var color: Color = Theme.warmOrange var radius: CGFloat = 8 func body(content: Content) -> some View { content .shadow(color: color.opacity(0.5), radius: radius / 2, y: 0) .shadow(color: color.opacity(0.3), radius: radius, y: 0) } } extension View { func glowEffect(color: Color = Theme.warmOrange, radius: CGFloat = 8) -> some View { modifier(GlowEffect(color: color, radius: radius)) } } // MARK: - Pressable Button Style struct PressableButtonStyle: ButtonStyle { var scale: CGFloat = 0.96 func makeBody(configuration: Configuration) -> some View { configuration.label .scaleEffect(configuration.isPressed ? scale : 1.0) .animation(Theme.Animation.spring, value: configuration.isPressed) } } extension View { func pressableStyle(scale: CGFloat = 0.96) -> some View { buttonStyle(PressableButtonStyle(scale: scale)) } } // MARK: - Shimmer Effect Modifier struct ShimmerEffect: ViewModifier { @State private var phase: CGFloat = 0 func body(content: Content) -> some View { content .overlay { GeometryReader { geo in LinearGradient( colors: [ .clear, Color.white.opacity(0.3), .clear ], startPoint: .leading, endPoint: .trailing ) .frame(width: geo.size.width * 2) .offset(x: -geo.size.width + (geo.size.width * 2 * phase)) } .mask(content) } .onAppear { withAnimation(.linear(duration: 1.5).repeatForever(autoreverses: false)) { phase = 1 } } } } extension View { func shimmer() -> some View { modifier(ShimmerEffect()) } } // MARK: - Staggered Animation Modifier struct StaggeredAnimation: ViewModifier { var index: Int var delay: Double = Theme.Animation.staggerDelay @State private var appeared = false func body(content: Content) -> some View { content .opacity(appeared ? 1 : 0) .offset(y: appeared ? 0 : 20) .onAppear { withAnimation(Theme.Animation.spring.delay(Double(index) * delay)) { appeared = true } } } } extension View { func staggeredAnimation(index: Int, delay: Double = Theme.Animation.staggerDelay) -> some View { modifier(StaggeredAnimation(index: index, delay: delay)) } } // MARK: - Badge Style Modifier struct BadgeStyle: ViewModifier { @Environment(\.colorScheme) private var colorScheme var color: Color = Theme.warmOrange var filled: Bool = true func body(content: Content) -> some View { content .font(.caption) .padding(.horizontal, 10) .padding(.vertical, 5) .background(filled ? color : color.opacity(0.2)) .foregroundStyle(filled ? .white : color) .clipShape(Capsule()) } } extension View { func badgeStyle(color: Color = Theme.warmOrange, filled: Bool = true) -> some View { modifier(BadgeStyle(color: color, filled: filled)) } } // MARK: - Section Header Style struct SectionHeaderStyle: ViewModifier { @Environment(\.colorScheme) private var colorScheme func body(content: Content) -> some View { content .font(.title2) .foregroundStyle(Theme.textPrimary(colorScheme)) } } extension View { func sectionHeaderStyle() -> some View { modifier(SectionHeaderStyle()) } } // MARK: - Themed Background Modifier struct ThemedBackground: ViewModifier { @Environment(\.colorScheme) private var colorScheme func body(content: Content) -> some View { content .background(Theme.backgroundGradient(colorScheme)) } } extension View { func themedBackground() -> some View { modifier(ThemedBackground()) } } // MARK: - Sport Color Bar struct SportColorBar: View { let sport: Sport var body: some View { RoundedRectangle(cornerRadius: 2) .fill(sport.themeColor) .frame(width: 4) } } // MARK: - Sport Extension for Theme Colors extension Sport { var themeColor: Color { switch self { case .mlb: return Theme.mlbRed case .nba: return Theme.nbaOrange case .nhl: return Theme.nhlBlue case .nfl: return Theme.nflBrown case .mls: return Theme.mlsGreen case .wnba: return Theme.wnbaPurple case .nwsl: return Theme.nwslTeal } } }