Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
e1655ce20c
commit
ca742eb73f
@ -30,22 +30,34 @@ struct CardStackView: View {
|
|||||||
/// This prevents width changes during dealing by pre-allocating placeholder space.
|
/// This prevents width changes during dealing by pre-allocating placeholder space.
|
||||||
let minimumCardSlots: Int
|
let minimumCardSlots: Int
|
||||||
|
|
||||||
|
/// Whether placeholders should use staggered (overlapped) layout like dealt cards.
|
||||||
|
/// - `true`: Placeholders overlap using `cardSpacing` (use for bordered containers like player hand)
|
||||||
|
/// - `false`: Placeholders side-by-side (use for borderless containers like dealer hand)
|
||||||
|
let staggeredPlaceholders: Bool
|
||||||
|
|
||||||
/// Number of additional placeholders needed to reach minimum slots.
|
/// Number of additional placeholders needed to reach minimum slots.
|
||||||
private var placeholdersNeeded: Int {
|
private var placeholdersNeeded: Int {
|
||||||
max(0, minimumCardSlots - cards.count)
|
max(0, minimumCardSlots - cards.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spacing to use for empty placeholder state
|
||||||
|
private var placeholderSpacing: CGFloat {
|
||||||
|
staggeredPlaceholders ? cardSpacing : Design.Spacing.small
|
||||||
|
}
|
||||||
|
|
||||||
/// Scaled animation duration based on dealing speed.
|
/// Scaled animation duration based on dealing speed.
|
||||||
private var animationDuration: Double {
|
private var animationDuration: Double {
|
||||||
Design.Animation.springDuration * dealingSpeed
|
Design.Animation.springDuration * dealingSpeed
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: cards.isEmpty && placeholdersNeeded > 0 ? Design.Spacing.small : cardSpacing) {
|
// Use staggered or side-by-side spacing based on configuration
|
||||||
|
HStack(spacing: cards.isEmpty && placeholdersNeeded > 0 ? placeholderSpacing : cardSpacing) {
|
||||||
if cards.isEmpty && placeholdersNeeded > 0 {
|
if cards.isEmpty && placeholdersNeeded > 0 {
|
||||||
// Show placeholders when empty
|
// Show placeholders when empty
|
||||||
ForEach(0..<placeholdersNeeded, id: \.self) { _ in
|
ForEach(0..<placeholdersNeeded, id: \.self) { index in
|
||||||
CardPlaceholderView(width: cardWidth)
|
CardPlaceholderView(width: cardWidth)
|
||||||
|
.zIndex(Double(index))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Show actual cards
|
// Show actual cards
|
||||||
@ -95,6 +107,7 @@ struct CardStackView: View {
|
|||||||
|
|
||||||
extension CardStackView {
|
extension CardStackView {
|
||||||
/// Creates a card stack for the dealer (with hole card support).
|
/// Creates a card stack for the dealer (with hole card support).
|
||||||
|
/// Uses side-by-side placeholders since dealer area has no border.
|
||||||
static func dealer(
|
static func dealer(
|
||||||
cards: [Card],
|
cards: [Card],
|
||||||
showHoleCard: Bool,
|
showHoleCard: Bool,
|
||||||
@ -116,11 +129,13 @@ extension CardStackView {
|
|||||||
x: Design.DealAnimation.dealerOffsetX,
|
x: Design.DealAnimation.dealerOffsetX,
|
||||||
y: Design.DealAnimation.dealerOffsetY
|
y: Design.DealAnimation.dealerOffsetY
|
||||||
),
|
),
|
||||||
minimumCardSlots: 2
|
minimumCardSlots: 2,
|
||||||
|
staggeredPlaceholders: false // Side-by-side (no border to show shrink)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a card stack for the player (all cards face up).
|
/// Creates a card stack for the player (all cards face up).
|
||||||
|
/// Uses staggered placeholders to match dealt card layout (prevents container shrink).
|
||||||
/// - Parameter minimumCardSlots: Minimum slots to reserve (default 2 for initial deal).
|
/// - Parameter minimumCardSlots: Minimum slots to reserve (default 2 for initial deal).
|
||||||
static func player(
|
static func player(
|
||||||
cards: [Card],
|
cards: [Card],
|
||||||
@ -143,14 +158,15 @@ extension CardStackView {
|
|||||||
x: Design.DealAnimation.playerOffsetX,
|
x: Design.DealAnimation.playerOffsetX,
|
||||||
y: Design.DealAnimation.playerOffsetY
|
y: Design.DealAnimation.playerOffsetY
|
||||||
),
|
),
|
||||||
minimumCardSlots: minimumCardSlots
|
minimumCardSlots: minimumCardSlots,
|
||||||
|
staggeredPlaceholders: true // Staggered (bordered container shows shrink)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
|
||||||
#Preview("Empty") {
|
#Preview("Empty - Staggered") {
|
||||||
ZStack {
|
ZStack {
|
||||||
Color.Table.felt.ignoresSafeArea()
|
Color.Table.felt.ignoresSafeArea()
|
||||||
CardStackView(
|
CardStackView(
|
||||||
@ -162,7 +178,26 @@ extension CardStackView {
|
|||||||
showCardCount: false,
|
showCardCount: false,
|
||||||
isFaceUp: { _ in true },
|
isFaceUp: { _ in true },
|
||||||
dealOffset: .zero,
|
dealOffset: .zero,
|
||||||
minimumCardSlots: 2
|
minimumCardSlots: 2,
|
||||||
|
staggeredPlaceholders: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview("Empty - Side by Side") {
|
||||||
|
ZStack {
|
||||||
|
Color.Table.felt.ignoresSafeArea()
|
||||||
|
CardStackView(
|
||||||
|
cards: [],
|
||||||
|
cardWidth: 60,
|
||||||
|
cardSpacing: -20,
|
||||||
|
showAnimations: true,
|
||||||
|
dealingSpeed: 1.0,
|
||||||
|
showCardCount: false,
|
||||||
|
isFaceUp: { _ in true },
|
||||||
|
dealOffset: .zero,
|
||||||
|
minimumCardSlots: 2,
|
||||||
|
staggeredPlaceholders: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user