CasinoGames/Blackjack/Views/Table/BlackjackTableView.swift

113 lines
4.2 KiB
Swift

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