Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-01-24 16:37:07 -06:00
parent 109b2416fc
commit 32bff901c7
2 changed files with 39 additions and 74 deletions

View File

@ -103,11 +103,11 @@ struct CardsDisplayArea: View {
let percentage: CGFloat = isLandscape ? 0.175 : height < 700 ? 0.14 : 0.18 let percentage: CGFloat = isLandscape ? 0.175 : height < 700 ? 0.14 : 0.18
let cardWidth = height * percentage let cardWidth = height * percentage
// CompactHandView: cardWidth = containerWidth / divisor // CompactHandView uses: cardWidth = (containerWidth - 2 * spacing) / 3
let overlapRatio: CGFloat = -0.45 // So: containerWidth = 3 * cardWidth + 2 * spacing
let spacing = Design.Spacing.medium
let maxCards: CGFloat = 3 let maxCards: CGFloat = 3
let divisor = 1 + (maxCards - 1) * (1 + overlapRatio) return (cardWidth * maxCards) + (2 * spacing)
return cardWidth * divisor
} }
/// Current hand section width based on mode /// Current hand section width based on mode

View File

@ -2,13 +2,13 @@
// CompactHandView.swift // CompactHandView.swift
// Baccarat // Baccarat
// //
// A compact view showing cards in a horizontal row with overlap. // A compact view showing cards in a horizontal row.
// //
import SwiftUI import SwiftUI
import CasinoKit import CasinoKit
/// A compact hand view showing cards in a row with overlap. /// A compact hand view showing cards in a row.
struct CompactHandView: View { struct CompactHandView: View {
let cards: [Card] let cards: [Card]
let cardsFaceUp: [Bool] let cardsFaceUp: [Bool]
@ -34,17 +34,14 @@ struct CompactHandView: View {
// MARK: - Constants // MARK: - Constants
/// Overlap ratio relative to card width (negative = overlap)
private let overlapRatio: CGFloat = -0.45
/// Maximum number of cards in baccarat hand /// Maximum number of cards in baccarat hand
private let maxCards: Int = 3 private let maxCards: Int = 3
/// Placeholder spacing when no cards /// Placeholder spacing when no cards
private let placeholderSpacing: CGFloat = Design.Spacing.small private let placeholderSpacing: CGFloat = Design.Spacing.small
/// Spacing for interactive layout /// Spacing between cards
private let spacedGap: CGFloat = Design.Spacing.medium private let cardSpacing: CGFloat = Design.Spacing.medium
// MARK: - Computed Properties // MARK: - Computed Properties
@ -59,18 +56,12 @@ struct CompactHandView: View {
} }
/// Card width calculated from container width /// Card width calculated from container width
/// Formula: containerWidth = cardWidth + (cardWidth + overlap) * 2 /// Formula accounts for spacing between 3 cards
/// Where overlap = cardWidth * overlapRatio
private var cardWidth: CGFloat { private var cardWidth: CGFloat {
// Use a fixed overlap ratio for sizing to keep cards large // containerWidth = 3 * cardWidth + 2 * spacing
let baseOverlapRatio: CGFloat = -0.45 // cardWidth = (containerWidth - 2 * spacing) / 3
let divisor = 1 + CGFloat(maxCards - 1) * (1 + baseOverlapRatio) let spacing = cardSpacing
return containerWidth / divisor return max(50, (containerWidth - 2 * spacing) / CGFloat(maxCards))
}
/// Card overlap based on card width
private var cardOverlap: CGFloat {
cardWidth * overlapRatio
} }
/// Card height based on aspect ratio /// Card height based on aspect ratio
@ -78,26 +69,9 @@ struct CompactHandView: View {
cardWidth * CasinoDesign.Size.cardAspectRatio cardWidth * CasinoDesign.Size.cardAspectRatio
} }
/// Whether to use a spaced layout for interaction
private var useSpacedLayout: Bool {
revealStyle == .tap || revealStyle == .squeeze
}
/// The effective spacing for the card stack /// The effective spacing for the card stack
private var effectiveSpacing: CGFloat { private var effectiveSpacing: CGFloat {
if cards.isEmpty { cards.isEmpty ? placeholderSpacing : cardSpacing
return placeholderSpacing
} else if useSpacedLayout {
return spacedGap
} else {
return cardOverlap
}
}
/// Total width required for the spaced layout
private var totalSpacedWidth: CGFloat {
let count = CGFloat(max(cards.count, 2))
return (count * cardWidth) + ((count - 1) * effectiveSpacing)
} }
// MARK: - Body // MARK: - Body
@ -106,43 +80,34 @@ struct CompactHandView: View {
GeometryReader { geometry in GeometryReader { geometry in
let availableWidth = geometry.size.width let availableWidth = geometry.size.width
Group { ScrollViewReader { proxy in
if useSpacedLayout { ScrollView(.horizontal, showsIndicators: false) {
// Always use ScrollView in interactive modes for stable view hierarchy cardsContent
ScrollViewReader { proxy in .padding(.horizontal, Design.Spacing.medium)
ScrollView(.horizontal, showsIndicators: false) { .frame(minWidth: availableWidth, alignment: .center)
cardsContent .id("cards_container")
.padding(.horizontal, Design.Spacing.medium) }
.frame(minWidth: availableWidth, alignment: .center) .scrollDisabled(cards.count < 3)
.id("cards_container") .scrollClipDisabled(true) // Prevent clipping during deal animations
} .background(Color.clear)
.scrollDisabled(cards.count < 3) .onChange(of: cards.count) { _, newCount in
.scrollClipDisabled(true) // Prevent clipping during deal animations // When 3rd card is dealt, wait for animation then scroll
.background(Color.clear) if newCount == 3 {
.onChange(of: cards.count) { _, newCount in let lastIndex = isBottom ? 4 : 5
// When 3rd card is dealt, wait for animation then scroll DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
if newCount == 3 { withAnimation(.spring(duration: 0.5)) {
let lastIndex = isBottom ? 4 : 5 proxy.scrollTo(lastIndex, anchor: .center)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
withAnimation(.spring(duration: 0.5)) {
proxy.scrollTo(lastIndex, anchor: .center)
}
}
}
}
.onChange(of: currentRevealIndex) { _, newIndex in
// Scroll to the active card during interaction
if newIndex >= 4 {
withAnimation(.spring(duration: 0.5)) {
proxy.scrollTo(newIndex, anchor: .center)
}
} }
} }
} }
} else { }
// Regular centered layout for non-interactive modes .onChange(of: currentRevealIndex) { _, newIndex in
cardsContent // Scroll to the active card during interaction
.frame(maxWidth: .infinity, alignment: .center) if newIndex >= 4 {
withAnimation(.spring(duration: 0.5)) {
proxy.scrollTo(newIndex, anchor: .center)
}
}
} }
} }
} }