// // BlackjackTableView.swift // Blackjack // // The main table layout showing dealer and player hands. // import SwiftUI import CasinoKit struct BlackjackTableView: View { @Bindable var state: GameState let onPlaceBet: () -> Void /// Whether to show Hi-Lo card count values on cards. var showCardCount: Bool { state.settings.showCardCount } // MARK: - Scaled Metrics @ScaledMetric(relativeTo: .headline) private var labelFontSize: CGFloat = Design.BaseFontSize.medium @ScaledMetric(relativeTo: .title) private var valueFontSize: CGFloat = Design.BaseFontSize.xLarge @ScaledMetric(relativeTo: .caption) private var hintFontSize: CGFloat = Design.BaseFontSize.small // MARK: - Layout private let cardWidth: CGFloat = Design.Size.cardWidth private let cardSpacing: CGFloat = Design.Size.cardOverlap /// Fixed height for the hint area to prevent layout shifts private let hintAreaHeight: CGFloat = 44 // Use global debug flag from Design constants private var showDebugBorders: Bool { Design.showDebugBorders } var body: some View { VStack(spacing: Design.Spacing.small) { // Dealer area DealerHandView( hand: state.dealerHand, showHoleCard: state.shouldShowDealerHoleCard, showCardCount: showCardCount, cardWidth: cardWidth, cardSpacing: cardSpacing ) .debugBorder(showDebugBorders, color: .red, label: "Dealer") // Flexible space between dealer and player (minimum 60pt) Spacer(minLength: 60) .debugBorder(showDebugBorders, color: .yellow, label: "Spacer") // Player hands area - only show when there are cards dealt if state.playerHands.first?.cards.isEmpty == false { PlayerHandsView( hands: state.playerHands, activeHandIndex: state.activeHandIndex, isPlayerTurn: state.isPlayerTurn, showCardCount: showCardCount, cardWidth: cardWidth, cardSpacing: cardSpacing ) .transition(.opacity) .debugBorder(showDebugBorders, color: .green, label: "Player") } // Betting zone (when betting) if state.currentPhase == .betting { Spacer() .debugBorder(showDebugBorders, color: .yellow, label: "Spacer2") BettingZoneView( betAmount: state.currentBet, minBet: state.settings.minBet, maxBet: state.settings.maxBet, onTap: onPlaceBet ) .transition(.scale.combined(with: .opacity)) .debugBorder(showDebugBorders, color: .blue, label: "BetZone") // Betting hint based on count (only when card counting enabled) if let hint = state.bettingHint { BettingHintView(hint: hint, trueCount: state.engine.trueCount) .transition(.opacity) .debugBorder(showDebugBorders, color: .purple, label: "BetHint") } } else { // Fixed-height hint area to prevent layout shifts during player turn ZStack { if let hint = state.currentHint { HintView(hint: hint) .transition(.opacity) } } .frame(height: hintAreaHeight) .debugBorder(showDebugBorders, color: .orange, label: "HintArea") } } .padding(.horizontal, Design.Spacing.large) .padding(.vertical, Design.Spacing.medium) .debugBorder(showDebugBorders, color: .white, label: "TableView") .animation(.spring(duration: Design.Animation.springDuration), value: state.currentPhase) } } // MARK: - Previews #Preview { ZStack { Color.Table.felt.ignoresSafeArea() Text("Use GameTableView for full preview") .foregroundStyle(.white) } }