153 lines
5.0 KiB
Swift
153 lines
5.0 KiB
Swift
//
|
|
// RoadMapView.swift
|
|
// Baccarat
|
|
//
|
|
// A visual history display of recent game results (Big Road style).
|
|
//
|
|
|
|
import SwiftUI
|
|
import CasinoKit
|
|
|
|
/// A simple road display showing recent results.
|
|
struct RoadMapView: View {
|
|
let results: [RoundResult]
|
|
|
|
// MARK: - Scaled Fonts (Dynamic Type)
|
|
|
|
@ScaledMetric(relativeTo: .caption2) private var historyFontSize: CGFloat = Design.BaseFontSize.small
|
|
@ScaledMetric(relativeTo: .caption2) private var dotSize: CGFloat = 22
|
|
|
|
// MARK: - Accessibility
|
|
|
|
private var historySummary: String {
|
|
if results.isEmpty {
|
|
return String(localized: "No history yet")
|
|
}
|
|
|
|
let playerWins = results.filter { $0.result == .playerWins }.count
|
|
let bankerWins = results.filter { $0.result == .bankerWins }.count
|
|
let ties = results.filter { $0.result == .tie }.count
|
|
|
|
let format = String(localized: "historySummaryFormat")
|
|
return String(format: format, results.count, playerWins, bankerWins, ties)
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
|
Text("HISTORY")
|
|
.font(.system(size: historyFontSize, weight: .bold, design: .rounded))
|
|
.foregroundStyle(.white.opacity(Design.Opacity.accent))
|
|
.tracking(1)
|
|
|
|
ScrollView(.horizontal) {
|
|
HStack(spacing: Design.Spacing.xSmall) {
|
|
ForEach(results) { result in
|
|
RoadDot(
|
|
result: result.result,
|
|
dotSize: dotSize,
|
|
hasPair: result.hasPair,
|
|
isNatural: result.isNatural
|
|
)
|
|
}
|
|
}
|
|
.padding(.vertical, Design.Spacing.xSmall)
|
|
}
|
|
.scrollIndicators(.hidden)
|
|
}
|
|
.padding(.horizontal, Design.Spacing.medium)
|
|
.padding(.vertical, Design.Spacing.small)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: Design.CornerRadius.small)
|
|
.fill(Color.black.opacity(Design.Opacity.light))
|
|
)
|
|
.accessibilityElement(children: .ignore)
|
|
.accessibilityLabel(String(localized: "Game history"))
|
|
.accessibilityValue(historySummary)
|
|
}
|
|
}
|
|
|
|
/// A single dot in the road display.
|
|
struct RoadDot: View {
|
|
let result: GameResult
|
|
let dotSize: CGFloat
|
|
var hasPair: Bool = false
|
|
var isNatural: Bool = false
|
|
|
|
// MARK: - Layout Constants
|
|
|
|
private var markerSize: CGFloat { dotSize * 0.3 }
|
|
|
|
// MARK: - Scaled Fonts (Dynamic Type)
|
|
|
|
@ScaledMetric(relativeTo: .caption2) private var labelFontSize: CGFloat = Design.BaseFontSize.small
|
|
|
|
private var color: Color {
|
|
switch result {
|
|
case .playerWins: return .blue
|
|
case .bankerWins: return .red
|
|
case .tie: return .green
|
|
}
|
|
}
|
|
|
|
private var label: String {
|
|
switch result {
|
|
case .playerWins: return "P"
|
|
case .bankerWins: return "B"
|
|
case .tie: return "T"
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
Circle()
|
|
.fill(color)
|
|
.frame(width: dotSize, height: dotSize)
|
|
|
|
Circle()
|
|
.strokeBorder(Color.white.opacity(Design.Opacity.light), lineWidth: Design.LineWidth.thin)
|
|
.frame(width: dotSize, height: dotSize)
|
|
|
|
Text(label)
|
|
.font(.system(size: labelFontSize, weight: .bold))
|
|
.foregroundStyle(.white)
|
|
|
|
// Pair marker (bottom-left corner)
|
|
if hasPair {
|
|
Circle()
|
|
.fill(Color.yellow)
|
|
.frame(width: markerSize, height: markerSize)
|
|
.overlay(
|
|
Circle()
|
|
.strokeBorder(Color.white, lineWidth: Design.LineWidth.thin)
|
|
)
|
|
.offset(x: -dotSize * 0.35, y: dotSize * 0.35)
|
|
}
|
|
|
|
// Natural marker (top-right corner - star shape)
|
|
if isNatural {
|
|
Image(systemName: "star.fill")
|
|
.font(.system(size: markerSize))
|
|
.foregroundStyle(.yellow)
|
|
.offset(x: dotSize * 0.35, y: -dotSize * 0.35)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
ZStack {
|
|
Color.Table.preview
|
|
.ignoresSafeArea()
|
|
|
|
RoadMapView(results: [
|
|
RoundResult(result: .playerWins, playerValue: 8, bankerValue: 6, playerPair: true),
|
|
RoundResult(result: .bankerWins, playerValue: 4, bankerValue: 7),
|
|
RoundResult(result: .tie, playerValue: 5, bankerValue: 5, bankerPair: true),
|
|
RoundResult(result: .playerWins, playerValue: 9, bankerValue: 3),
|
|
RoundResult(result: .bankerWins, playerValue: 2, bankerValue: 8, playerPair: true, bankerPair: true)
|
|
])
|
|
.padding()
|
|
}
|
|
}
|
|
|