CasinoGames/Blackjack/Blackjack/Views/Table/SideBetZoneView.swift

153 lines
4.8 KiB
Swift

//
// SideBetZoneView.swift
// Blackjack
//
// Side bet zone for Perfect Pairs and 21+3.
//
import SwiftUI
import CasinoKit
/// A tappable zone for placing side bets.
struct SideBetZoneView: View {
let betType: SideBetType
let betAmount: Int
let isEnabled: Bool
let isAtMax: Bool
let onTap: () -> Void
@ScaledMetric(relativeTo: .callout) private var labelFontSize: CGFloat = Design.Size.sideBetLabelFontSize
@ScaledMetric(relativeTo: .caption) private var payoutFontSize: CGFloat = Design.Size.sideBetPayoutFontSize
private var backgroundColor: Color {
switch betType {
case .perfectPairs:
return Color.SideBet.perfectPairs
case .twentyOnePlusThree:
return Color.SideBet.twentyOnePlusThree
}
}
private var payoutText: String {
switch betType {
case .perfectPairs:
return "25:1" // Best payout shown
case .twentyOnePlusThree:
return "100:1" // Best payout shown
}
}
var body: some View {
Button {
if isEnabled { onTap() }
} label: {
ZStack {
// Background
RoundedRectangle(cornerRadius: Design.CornerRadius.medium)
.fill(backgroundColor)
.overlay(
RoundedRectangle(cornerRadius: Design.CornerRadius.medium)
.strokeBorder(
Color.white.opacity(Design.Opacity.hint),
lineWidth: Design.LineWidth.medium
)
)
// Content
VStack(spacing: Design.Spacing.xxSmall) {
Text(betType.shortName)
.font(.system(size: labelFontSize, weight: .black, design: .rounded))
.foregroundStyle(.yellow)
Text(payoutText)
.font(.system(size: payoutFontSize, weight: .medium, design: .rounded))
.foregroundStyle(.white.opacity(Design.Opacity.strong))
}
// Chip indicator - top right
if betAmount > 0 {
VStack {
HStack {
Spacer()
ChipBadgeView(amount: betAmount, isMax: isAtMax)
.padding(Design.Spacing.xSmall)
}
Spacer()
}
}
}
}
.buttonStyle(.plain)
.opacity(isEnabled ? 1.0 : Design.Opacity.medium)
.accessibilityLabel("\(betType.displayName) bet, pays up to \(payoutText)")
.accessibilityHint(betAmount > 0 ? "Current bet $\(betAmount)" : "Double tap to place bet")
}
}
/// Small chip badge for side bet indicators.
struct ChipBadgeView: View {
let amount: Int
let isMax: Bool
var body: some View {
ZStack {
Circle()
.fill(isMax ? Color.gray : Color.yellow)
.frame(width: CasinoDesign.Size.chipBadge, height: CasinoDesign.Size.chipBadge)
Circle()
.strokeBorder(Color.white.opacity(Design.Opacity.almostFull), lineWidth: Design.LineWidth.thin)
.frame(width: CasinoDesign.Size.chipBadgeInner, height: CasinoDesign.Size.chipBadgeInner)
if isMax {
Text(String(localized: "MAX"))
.font(.system(size: Design.BaseFontSize.xxSmall, weight: .black))
.foregroundStyle(.white)
} else {
Text(formatCompact(amount))
.font(.system(size: Design.BaseFontSize.small, weight: .bold, design: .rounded))
.foregroundStyle(.black)
}
}
.shadow(color: .black.opacity(Design.Opacity.light), radius: Design.Shadow.radiusSmall, y: Design.Shadow.offsetSmall)
}
private func formatCompact(_ value: Int) -> String {
if value >= 1000 {
return "\(value / 1000)K"
}
return "\(value)"
}
}
// MARK: - Previews
#Preview("Perfect Pairs") {
ZStack {
Color.Table.felt.ignoresSafeArea()
SideBetZoneView(
betType: .perfectPairs,
betAmount: 0,
isEnabled: true,
isAtMax: false,
onTap: {}
)
.frame(width: 80, height: 70)
}
}
#Preview("21+3 with Bet") {
ZStack {
Color.Table.felt.ignoresSafeArea()
SideBetZoneView(
betType: .twentyOnePlusThree,
betAmount: 25,
isEnabled: true,
isAtMax: false,
onTap: {}
)
.frame(width: 80, height: 70)
}
}