// // ActionButtonsView.swift // Blackjack // // Container for game action buttons (betting, player turn). // import SwiftUI import CasinoKit struct ActionButtonsView: View { @Bindable var state: GameState // Scaled metrics @ScaledMetric(relativeTo: .headline) private var buttonFontSize: CGFloat = Design.BaseFontSize.large @ScaledMetric(relativeTo: .body) private var iconSize: CGFloat = Design.IconSize.large // Scaled container height - base 60pt, scales with accessibility @ScaledMetric(relativeTo: .body) private var containerHeight: CGFloat = 60 var body: some View { VStack(spacing: Design.Spacing.small) { // Primary actions HStack(spacing: Design.Spacing.medium) { switch state.currentPhase { case .betting: bettingButtons case .playerTurn: playerTurnButtons case .roundComplete: // Empty - handled by result banner EmptyView() default: // Dealing, dealer turn - show nothing EmptyView() } } .animation(.spring(duration: Design.Animation.quick), value: state.currentPhase) } .frame(minHeight: containerHeight) .padding(.horizontal, Design.Spacing.large) } // MARK: - Betting Phase Buttons @ViewBuilder private var bettingButtons: some View { if state.currentBet > 0 { VStack(spacing: Design.Spacing.small) { // Show hint if bet is below minimum if state.isBetBelowMinimum { Text(String(localized: "Add $\(state.amountNeededForMinimum) more to meet minimum")) .font(.system(size: Design.BaseFontSize.small, weight: .medium)) .foregroundStyle(.orange) .transition(.opacity) } HStack(spacing: Design.Spacing.medium) { ActionButton( String(localized: "Clear"), icon: "xmark.circle", style: .destructive ) { state.clearBet() } // Always show Deal button, but disable if below minimum ActionButton( String(localized: "Deal"), icon: "play.fill", style: .primary ) { Task { await state.deal() } } .opacity(state.canDeal ? 1.0 : Design.Opacity.medium) .disabled(!state.canDeal) } } } } // MARK: - Player Turn Buttons @ViewBuilder private var playerTurnButtons: some View { // All player actions in a single row HStack(spacing: Design.Spacing.medium) { if state.canHit { ActionButton( String(localized: "Hit"), style: .custom(Color.Button.hit) ) { Task { await state.hit() } } } if state.canStand { ActionButton( String(localized: "Stand"), style: .custom(Color.Button.stand) ) { Task { await state.stand() } } } if state.canDouble { ActionButton( String(localized: "Double"), style: .custom(Color.Button.doubleDown) ) { Task { await state.doubleDown() } } } if state.canSplit { ActionButton( String(localized: "Split"), style: .custom(Color.Button.split) ) { Task { await state.split() } } } if state.canSurrender { ActionButton( String(localized: "Surrender"), style: .custom(Color.Button.surrender) ) { Task { await state.surrender() } } } } } } // MARK: - Previews #Preview("Betting Phase - No Bet") { ZStack { Color.Table.felt.ignoresSafeArea() ActionButtonsView(state: { let state = GameState(settings: GameSettings()) return state }()) } } #Preview("Betting Phase - With Bet") { ZStack { Color.Table.felt.ignoresSafeArea() ActionButtonsView(state: { let state = GameState(settings: GameSettings()) state.placeBet(amount: 100) return state }()) } } #Preview("Betting Phase - Below Minimum") { ZStack { Color.Table.felt.ignoresSafeArea() ActionButtonsView(state: { let state = GameState(settings: GameSettings()) state.placeBet(amount: 25) return state }()) } } // MARK: - Player Turn Button Previews /// Preview helper that shows player turn button layouts without needing real game state. private struct PlayerTurnButtonsPreview: View { let showHit: Bool let showStand: Bool let showDouble: Bool let showSplit: Bool let showSurrender: Bool private let containerHeight: CGFloat = 120 var body: some View { ZStack { Color.clear .frame(height: containerHeight) // Single row of buttons matching actual game layout HStack(spacing: Design.Spacing.medium) { if showHit { ActionButton("Hit", style: .custom(Color.Button.hit)) {} } if showStand { ActionButton("Stand", style: .custom(Color.Button.stand)) {} } if showDouble { ActionButton("Double", style: .custom(Color.Button.doubleDown)) {} } if showSplit { ActionButton("Split", style: .custom(Color.Button.split)) {} } if showSurrender { ActionButton("Surrender", style: .custom(Color.Button.surrender)) {} } } } .padding(.horizontal, Design.Spacing.large) } } #Preview("Player Turn - Hit & Stand Only") { ZStack { Color.Table.felt.ignoresSafeArea() PlayerTurnButtonsPreview( showHit: true, showStand: true, showDouble: false, showSplit: false, showSurrender: false ) } } #Preview("Player Turn - With Double") { ZStack { Color.Table.felt.ignoresSafeArea() PlayerTurnButtonsPreview( showHit: true, showStand: true, showDouble: true, showSplit: false, showSurrender: false ) } } #Preview("Player Turn - With Split") { ZStack { Color.Table.felt.ignoresSafeArea() PlayerTurnButtonsPreview( showHit: true, showStand: true, showDouble: true, showSplit: true, showSurrender: false ) } } #Preview("Player Turn - With Surrender") { ZStack { Color.Table.felt.ignoresSafeArea() PlayerTurnButtonsPreview( showHit: true, showStand: true, showDouble: true, showSplit: false, showSurrender: true ) } } #Preview("Player Turn - All Options") { ZStack { Color.Table.felt.ignoresSafeArea() PlayerTurnButtonsPreview( showHit: true, showStand: true, showDouble: true, showSplit: true, showSurrender: true ) } } #Preview("Player Turn - After Hit (No Double/Split)") { ZStack { Color.Table.felt.ignoresSafeArea() PlayerTurnButtonsPreview( showHit: true, showStand: true, showDouble: false, showSplit: false, showSurrender: false ) } } #Preview("Player Turn - Split Hand (No Resplit)") { ZStack { Color.Table.felt.ignoresSafeArea() PlayerTurnButtonsPreview( showHit: true, showStand: true, showDouble: true, showSplit: false, showSurrender: false ) } }