118 lines
3.6 KiB
Swift
118 lines
3.6 KiB
Swift
//
|
|
// ChipOnTable.swift
|
|
// Baccarat
|
|
//
|
|
// A simplified chip display for showing bets on the table.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
/// A simplified chip for displaying bets on the table.
|
|
struct ChipOnTable: View {
|
|
let amount: Int
|
|
var showMax: Bool = false
|
|
|
|
// MARK: - Layout Constants
|
|
// Fixed sizes: chip face has strict space constraints
|
|
|
|
private let chipSize = Design.Size.chipSmall
|
|
private let innerRingSize: CGFloat = 26
|
|
private let gradientEndRadius: CGFloat = 20
|
|
private let maxBadgeFontSize = Design.BaseFontSize.xxSmall
|
|
private let maxBadgeOffsetX: CGFloat = 6
|
|
private let maxBadgeOffsetY: CGFloat = -4
|
|
|
|
// MARK: - Computed Properties
|
|
|
|
private var chipColor: Color {
|
|
switch amount {
|
|
case 0..<50: return .blue
|
|
case 50..<100: return .orange
|
|
case 100..<500: return .black
|
|
case 500..<1000: return .purple
|
|
default: return Color.Chip.gold
|
|
}
|
|
}
|
|
|
|
private var displayText: String {
|
|
amount >= 1000 ? "\(amount / 1000)K" : "\(amount)"
|
|
}
|
|
|
|
private var textFontSize: CGFloat {
|
|
amount >= 1000 ? Design.BaseFontSize.small : 11
|
|
}
|
|
|
|
// MARK: - Body
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
Circle()
|
|
.fill(
|
|
RadialGradient(
|
|
colors: [chipColor.opacity(0.9), chipColor],
|
|
center: .topLeading,
|
|
startRadius: 0,
|
|
endRadius: gradientEndRadius
|
|
)
|
|
)
|
|
.frame(width: chipSize, height: chipSize)
|
|
|
|
Circle()
|
|
.strokeBorder(Color.white.opacity(Design.Opacity.heavy), lineWidth: Design.LineWidth.medium)
|
|
.frame(width: chipSize, height: chipSize)
|
|
|
|
Circle()
|
|
.strokeBorder(Color.white.opacity(Design.Opacity.light), lineWidth: Design.LineWidth.thin)
|
|
.frame(width: innerRingSize, height: innerRingSize)
|
|
|
|
Text(displayText)
|
|
.font(.system(size: textFontSize, weight: .bold))
|
|
.foregroundStyle(.white)
|
|
}
|
|
.shadow(color: .black.opacity(Design.Opacity.light), radius: Design.Shadow.radiusSmall, x: 1, y: 2)
|
|
.overlay(alignment: .topTrailing) {
|
|
if showMax {
|
|
Text("MAX")
|
|
.font(.system(size: maxBadgeFontSize, weight: .black))
|
|
.foregroundStyle(.white)
|
|
.padding(.horizontal, Design.Spacing.xSmall)
|
|
.padding(.vertical, Design.Spacing.xxSmall)
|
|
.background(
|
|
Capsule()
|
|
.fill(Color.red)
|
|
)
|
|
.offset(x: maxBadgeOffsetX, y: maxBadgeOffsetY)
|
|
.accessibilityHidden(true) // Included in parent label
|
|
}
|
|
}
|
|
.accessibilityLabel(accessibilityDescription)
|
|
}
|
|
|
|
// MARK: - Accessibility
|
|
|
|
private var accessibilityDescription: String {
|
|
let format = String(localized: "betAmountFormat")
|
|
let base = String(format: format, amount.formatted())
|
|
if showMax {
|
|
return base + ", " + String(localized: "maximum bet")
|
|
}
|
|
return base
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
ZStack {
|
|
Color.Table.preview
|
|
.ignoresSafeArea()
|
|
|
|
HStack(spacing: Design.Spacing.xLarge) {
|
|
ChipOnTable(amount: 25)
|
|
ChipOnTable(amount: 100)
|
|
ChipOnTable(amount: 500)
|
|
ChipOnTable(amount: 1000)
|
|
ChipOnTable(amount: 5000, showMax: true)
|
|
}
|
|
}
|
|
}
|
|
|