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