style(blackjack): format code for better readability
Some checks failed
CI / Get Changed Files (pull_request) Successful in 6s
CI / eslint (pull_request) Failing after 8s
CI / prettier (pull_request) Successful in 20s
CI / Checkstyle Main (pull_request) Successful in 31s
CI / test-build (pull_request) Successful in 46s

This commit is contained in:
Jan-Marlon Leibl 2025-03-27 15:47:16 +01:00
commit 23888ceb27
Signed by: jleibl
GPG key ID: 300B2F906DC6F1D5
9 changed files with 139 additions and 121 deletions

View file

@ -5,17 +5,21 @@
<div class="lg:col-span-3 space-y-6 flex flex-col gap-4"> <div class="lg:col-span-3 space-y-6 flex flex-col gap-4">
<app-dealer-hand [cards]="dealerCards()"></app-dealer-hand> <app-dealer-hand [cards]="dealerCards()"></app-dealer-hand>
<app-player-hand [cards]="playerCards()"></app-player-hand> <app-player-hand [cards]="playerCards()"></app-player-hand>
<!-- Action message indicator --> <!-- Action message indicator -->
@if (isActionInProgress()) { @if (isActionInProgress()) {
<div class="flex justify-center"> <div class="flex justify-center">
<div class="card p-4 flex items-center gap-3 animate-pulse bg-deep-blue-light border border-deep-blue-light/50"> <div
<div class="w-5 h-5 rounded-full border-2 border-white border-t-transparent animate-spin"></div> class="card p-4 flex items-center gap-3 animate-pulse bg-deep-blue-light border border-deep-blue-light/50"
>
<div
class="w-5 h-5 rounded-full border-2 border-white border-t-transparent animate-spin"
></div>
<span>{{ currentAction() }}</span> <span>{{ currentAction() }}</span>
</div> </div>
</div> </div>
} }
@if (gameInProgress()) { @if (gameInProgress()) {
<app-game-controls <app-game-controls
[playerCards]="playerCards()" [playerCards]="playerCards()"

View file

@ -42,7 +42,7 @@ export default class BlackjackComponent {
gameInProgress = signal(false); gameInProgress = signal(false);
gameState = signal<string>('IN_PROGRESS'); gameState = signal<string>('IN_PROGRESS');
showGameResult = signal(false); showGameResult = signal(false);
// Add loading state trackers // Add loading state trackers
isActionInProgress = signal(false); isActionInProgress = signal(false);
currentAction = signal<string>(''); currentAction = signal<string>('');
@ -66,7 +66,7 @@ export default class BlackjackComponent {
// When game ends, make sure all dealer cards are visible // When game ends, make sure all dealer cards are visible
const isGameOver = game.state !== 'IN_PROGRESS'; const isGameOver = game.state !== 'IN_PROGRESS';
this.dealerCards.set( this.dealerCards.set(
game.dealerCards.map((card, index) => ({ game.dealerCards.map((card, index) => ({
...card, ...card,
@ -85,7 +85,7 @@ export default class BlackjackComponent {
if (isGameOver) { if (isGameOver) {
console.log('Game is over, state:', game.state); console.log('Game is over, state:', game.state);
this.refreshUserBalance(); this.refreshUserBalance();
// Show result immediately without resetting first // Show result immediately without resetting first
this.showGameResult.set(true); this.showGameResult.set(true);
console.log('Game result dialog should be shown now'); console.log('Game result dialog should be shown now');
@ -95,7 +95,7 @@ export default class BlackjackComponent {
onNewGame(bet: number): void { onNewGame(bet: number): void {
this.isActionInProgress.set(true); this.isActionInProgress.set(true);
this.currentAction.set('Spiel wird gestartet...'); this.currentAction.set('Spiel wird gestartet...');
this.blackjackService.startGame(bet).subscribe({ this.blackjackService.startGame(bet).subscribe({
next: (game) => { next: (game) => {
this.updateGameState(game); this.updateGameState(game);
@ -114,7 +114,7 @@ export default class BlackjackComponent {
this.isActionInProgress.set(true); this.isActionInProgress.set(true);
this.currentAction.set('Karte wird gezogen...'); this.currentAction.set('Karte wird gezogen...');
this.blackjackService.hit(this.currentGameId()!).subscribe({ this.blackjackService.hit(this.currentGameId()!).subscribe({
next: (game) => { next: (game) => {
this.updateGameState(game); this.updateGameState(game);
@ -130,7 +130,7 @@ export default class BlackjackComponent {
onStand(): void { onStand(): void {
if (!this.currentGameId() || this.isActionInProgress()) return; if (!this.currentGameId() || this.isActionInProgress()) return;
if (this.gameState() !== 'IN_PROGRESS') { if (this.gameState() !== 'IN_PROGRESS') {
console.log('Cannot stand: game is not in progress'); console.log('Cannot stand: game is not in progress');
return; return;
@ -138,7 +138,7 @@ export default class BlackjackComponent {
this.isActionInProgress.set(true); this.isActionInProgress.set(true);
this.currentAction.set('Dealer zieht Karten...'); this.currentAction.set('Dealer zieht Karten...');
this.blackjackService.stand(this.currentGameId()!).subscribe({ this.blackjackService.stand(this.currentGameId()!).subscribe({
next: (game) => { next: (game) => {
this.updateGameState(game); this.updateGameState(game);
@ -154,7 +154,7 @@ export default class BlackjackComponent {
onDoubleDown(): void { onDoubleDown(): void {
if (!this.currentGameId() || this.isActionInProgress()) return; if (!this.currentGameId() || this.isActionInProgress()) return;
if (this.gameState() !== 'IN_PROGRESS' || this.playerCards().length !== 2) { if (this.gameState() !== 'IN_PROGRESS' || this.playerCards().length !== 2) {
console.log('Cannot double down: game is not in progress or more than 2 cards'); console.log('Cannot double down: game is not in progress or more than 2 cards');
return; return;
@ -162,7 +162,7 @@ export default class BlackjackComponent {
this.isActionInProgress.set(true); this.isActionInProgress.set(true);
this.currentAction.set('Einsatz wird verdoppelt...'); this.currentAction.set('Einsatz wird verdoppelt...');
this.blackjackService.doubleDown(this.currentGameId()!).subscribe({ this.blackjackService.doubleDown(this.currentGameId()!).subscribe({
next: (game) => { next: (game) => {
this.updateGameState(game); this.updateGameState(game);
@ -181,20 +181,19 @@ export default class BlackjackComponent {
this.showGameResult.set(false); this.showGameResult.set(false);
} }
private handleGameError(error: any): void { private handleGameError(error: HttpErrorResponse): void {
if (error instanceof HttpErrorResponse) { if (error instanceof HttpErrorResponse) {
if (error.status === 400 && error.error?.error === 'Invalid state') { if (error.status === 400 && error.error?.error === 'Invalid state') {
this.gameInProgress.set(false); this.gameInProgress.set(false);
this.refreshUserBalance(); this.refreshUserBalance();
} } else if (error.status === 500) {
else if (error.status === 500) {
console.log('Server error occurred. The game may have been updated in another session.'); console.log('Server error occurred. The game may have been updated in another session.');
this.gameInProgress.set(false); this.gameInProgress.set(false);
this.refreshUserBalance(); this.refreshUserBalance();
if (this.currentGameId()) { if (this.currentGameId()) {
this.refreshGameState(this.currentGameId()!); this.refreshGameState(this.currentGameId()!);
} }
@ -209,7 +208,7 @@ export default class BlackjackComponent {
}, },
error: (err) => { error: (err) => {
console.error('Failed to refresh game state:', err); console.error('Failed to refresh game state:', err);
} },
}); });
} }

View file

@ -35,30 +35,30 @@ import { Card } from '../../models/blackjack.model';
export class DealerHandComponent implements OnChanges { export class DealerHandComponent implements OnChanges {
@Input() cards: Card[] = []; @Input() cards: Card[] = [];
cardsWithState: (Card & { isNew: boolean; id: string })[] = []; cardsWithState: (Card & { isNew: boolean; id: string })[] = [];
private lastCardCount = 0; private lastCardCount = 0;
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes['cards']) { if (changes['cards']) {
this.updateCardsWithState(); this.updateCardsWithState();
} }
} }
private updateCardsWithState(): void { private updateCardsWithState(): void {
const newCards = this.cards.length > this.lastCardCount; const newCards = this.cards.length > this.lastCardCount;
this.cardsWithState = this.cards.map((card, index) => { this.cardsWithState = this.cards.map((card, index) => {
// Consider a card new if it's added after the initial state and is the latest card // Consider a card new if it's added after the initial state and is the latest card
const isNew = newCards && index >= this.lastCardCount; const isNew = newCards && index >= this.lastCardCount;
return { return {
...card, ...card,
isNew, isNew,
// Generate a unique ID to help Angular track the cards // Generate a unique ID to help Angular track the cards
id: `${card.suit}-${card.rank}-${index}` id: `${card.suit}-${card.rank}-${index}`,
}; };
}); });
this.lastCardCount = this.cards.length; this.lastCardCount = this.cards.length;
} }
} }

View file

@ -10,7 +10,9 @@ import { Card } from '../../models/blackjack.model';
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex justify-center text-lg mb-5"> <div class="flex justify-center text-lg mb-5">
<div class="card p-4"> <div class="card p-4">
<div class="text-emerald font-bold mb-1">Deine Punkte: {{ calculateHandValue(playerCards) }}</div> <div class="text-emerald font-bold mb-1">
Deine Punkte: {{ calculateHandValue(playerCards) }}
</div>
<div class="text-text-secondary"> <div class="text-text-secondary">
Status: <span [class]="getStatusClass(gameState)">{{ getStatusText(gameState) }}</span> Status: <span [class]="getStatusClass(gameState)">{{ getStatusText(gameState) }}</span>
</div> </div>
@ -26,7 +28,9 @@ import { Card } from '../../models/blackjack.model';
<span [class.invisible]="isActionInProgress">Ziehen</span> <span [class.invisible]="isActionInProgress">Ziehen</span>
@if (isActionInProgress) { @if (isActionInProgress) {
<div class="absolute inset-0 flex items-center justify-center"> <div class="absolute inset-0 flex items-center justify-center">
<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div> <div
class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"
></div>
</div> </div>
} }
</button> </button>
@ -39,7 +43,9 @@ import { Card } from '../../models/blackjack.model';
<span [class.invisible]="isActionInProgress">Halten</span> <span [class.invisible]="isActionInProgress">Halten</span>
@if (isActionInProgress) { @if (isActionInProgress) {
<div class="absolute inset-0 flex items-center justify-center"> <div class="absolute inset-0 flex items-center justify-center">
<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div> <div
class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"
></div>
</div> </div>
} }
</button> </button>
@ -52,7 +58,9 @@ import { Card } from '../../models/blackjack.model';
<span [class.invisible]="isActionInProgress">Verdoppeln</span> <span [class.invisible]="isActionInProgress">Verdoppeln</span>
@if (isActionInProgress) { @if (isActionInProgress) {
<div class="absolute inset-0 flex items-center justify-center"> <div class="absolute inset-0 flex items-center justify-center">
<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div> <div
class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"
></div>
</div> </div>
} }
</button> </button>
@ -71,9 +79,9 @@ import { Card } from '../../models/blackjack.model';
}) })
export class GameControlsComponent { export class GameControlsComponent {
@Input() playerCards: Card[] = []; @Input() playerCards: Card[] = [];
@Input() gameState: string = 'IN_PROGRESS'; @Input() gameState = 'IN_PROGRESS';
@Input() isActionInProgress: boolean = false; @Input() isActionInProgress = false;
@Output() hit = new EventEmitter<void>(); @Output() hit = new EventEmitter<void>();
@Output() stand = new EventEmitter<void>(); @Output() stand = new EventEmitter<void>();
@Output() doubleDown = new EventEmitter<void>(); @Output() doubleDown = new EventEmitter<void>();
@ -82,7 +90,7 @@ export class GameControlsComponent {
calculateHandValue(cards: Card[]): number { calculateHandValue(cards: Card[]): number {
let sum = 0; let sum = 0;
let aceCount = 0; let aceCount = 0;
const rankValues: Record<string, number> = { const rankValues: Record<string, number> = {
TWO: 2, TWO: 2,
THREE: 3, THREE: 3,
@ -96,9 +104,9 @@ export class GameControlsComponent {
JACK: 10, JACK: 10,
QUEEN: 10, QUEEN: 10,
KING: 10, KING: 10,
ACE: 11 ACE: 11,
}; };
for (const card of cards) { for (const card of cards) {
if (!card.hidden) { if (!card.hidden) {
const value = rankValues[card.rank] || 0; const value = rankValues[card.rank] || 0;
@ -108,12 +116,12 @@ export class GameControlsComponent {
} }
} }
} }
while (sum > 21 && aceCount > 0) { while (sum > 21 && aceCount > 0) {
sum -= 10; sum -= 10;
aceCount--; aceCount--;
} }
return sum; return sum;
} }

View file

@ -92,7 +92,9 @@ import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angula
<span [class.invisible]="isActionInProgress">Neues Spiel</span> <span [class.invisible]="isActionInProgress">Neues Spiel</span>
@if (isActionInProgress) { @if (isActionInProgress) {
<div class="absolute inset-0 flex items-center justify-center"> <div class="absolute inset-0 flex items-center justify-center">
<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div> <div
class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"
></div>
</div> </div>
} }
</button> </button>

View file

@ -8,9 +8,8 @@ describe('GameResultComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [GameResultComponent] imports: [GameResultComponent],
}) }).compileComponents();
.compileComponents();
fixture = TestBed.createComponent(GameResultComponent); fixture = TestBed.createComponent(GameResultComponent);
component = fixture.componentInstance; component = fixture.componentInstance;

View file

@ -7,55 +7,49 @@ import { animate, style, transition, trigger } from '@angular/animations';
standalone: true, standalone: true,
imports: [CommonModule, CurrencyPipe], imports: [CommonModule, CurrencyPipe],
template: ` template: `
<div <div *ngIf="visible" [@fadeInOut] class="modal-bg" style="z-index: 1000; position: fixed;">
*ngIf="visible" <div class="modal-card" [@cardAnimation]>
[@fadeInOut]
class="modal-bg"
style="z-index: 1000; position: fixed;"
>
<div
class="modal-card"
[@cardAnimation]
>
<h2 class="modal-heading" [class]="getResultClass()">{{ getResultTitle() }}</h2> <h2 class="modal-heading" [class]="getResultClass()">{{ getResultTitle() }}</h2>
<p class="py-2 text-text-secondary mb-4">{{ getResultMessage() }}</p> <p class="py-2 text-text-secondary mb-4">{{ getResultMessage() }}</p>
<div class="bg-deep-blue-light/50 rounded-lg p-5 mb-6 shadow-inner border border-deep-blue-light/30"> <div
class="bg-deep-blue-light/50 rounded-lg p-5 mb-6 shadow-inner border border-deep-blue-light/30"
>
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<div class="text-text-secondary">Einsatz:</div> <div class="text-text-secondary">Einsatz:</div>
<div class="font-medium text-right">{{ amount }} </div> <div class="font-medium text-right">{{ amount }} </div>
<div class="text-text-secondary">{{ isDraw ? 'Zurückgegeben:' : (isWin ? 'Gewonnen:' : 'Verloren:') }}</div> <div class="text-text-secondary">
<div {{ isDraw ? 'Zurückgegeben:' : isWin ? 'Gewonnen:' : 'Verloren:' }}
</div>
<div
class="font-medium text-right" class="font-medium text-right"
[ngClass]="{ [ngClass]="{
'text-emerald': isWin, 'text-emerald': isWin,
'text-accent-red': isLoss, 'text-accent-red': isLoss,
'text-yellow-400': isDraw 'text-yellow-400': isDraw,
}" }"
> >
{{ isLoss ? '-' : '+' }}{{ amount }} {{ isLoss ? '-' : '+' }}{{ amount }}
</div> </div>
<div class="text-text-secondary border-t border-text-secondary/20 pt-3 font-medium">Gesamt:</div> <div class="text-text-secondary border-t border-text-secondary/20 pt-3 font-medium">
<div Gesamt:
</div>
<div
class="font-medium text-right border-t border-text-secondary/20 pt-3" class="font-medium text-right border-t border-text-secondary/20 pt-3"
[ngClass]="{ [ngClass]="{
'text-emerald': isWin, 'text-emerald': isWin,
'text-accent-red': isLoss, 'text-accent-red': isLoss,
'text-white': isDraw 'text-white': isDraw,
}" }"
> >
{{ isWin ? '+' : (isLoss ? '-' : '') }}{{ amount }} {{ isWin ? '+' : isLoss ? '-' : '' }}{{ amount }}
</div> </div>
</div> </div>
</div> </div>
<button <button type="button" (click)="closeDialog()" class="button-primary w-full py-2">
type="button"
(click)="closeDialog()"
class="button-primary w-full py-2"
>
Verstanden Verstanden
</button> </button>
</div> </div>
@ -67,68 +61,66 @@ import { animate, style, transition, trigger } from '@angular/animations';
trigger('fadeInOut', [ trigger('fadeInOut', [
transition(':enter', [ transition(':enter', [
style({ opacity: 0 }), style({ opacity: 0 }),
animate('150ms ease-out', style({ opacity: 1 })) animate('150ms ease-out', style({ opacity: 1 })),
]), ]),
transition(':leave', [ transition(':leave', [animate('150ms ease-in', style({ opacity: 0 }))]),
animate('150ms ease-in', style({ opacity: 0 }))
])
]), ]),
trigger('cardAnimation', [ trigger('cardAnimation', [
transition(':enter', [ transition(':enter', [
style({ opacity: 0, transform: 'scale(0.95)' }), style({ opacity: 0, transform: 'scale(0.95)' }),
animate('200ms ease-out', style({ opacity: 1, transform: 'scale(1)' })) animate('200ms ease-out', style({ opacity: 1, transform: 'scale(1)' })),
]) ]),
]) ]),
] ],
}) })
export class GameResultComponent { export class GameResultComponent {
@Input() gameState: string = ''; @Input() gameState = '';
@Input() amount: number = 0; @Input() amount = 0;
@Input() set show(value: boolean) { @Input() set show(value: boolean) {
console.log('GameResultComponent show input changed:', value, 'gameState:', this.gameState); console.log('GameResultComponent show input changed:', value, 'gameState:', this.gameState);
this.visible = value; this.visible = value;
} }
@Output() close = new EventEmitter<void>(); @Output() gameResultClosed = new EventEmitter<void>();
visible = false; visible = false;
get isWin(): boolean { get isWin(): boolean {
return this.gameState === 'PLAYER_WON'; return this.gameState === 'PLAYER_WON';
} }
get isLoss(): boolean { get isLoss(): boolean {
return this.gameState === 'PLAYER_LOST'; return this.gameState === 'PLAYER_LOST';
} }
get isDraw(): boolean { get isDraw(): boolean {
return this.gameState === 'DRAW'; return this.gameState === 'DRAW';
} }
getResultTitle(): string { getResultTitle(): string {
if (this.isWin) return 'Gewonnen!'; if (this.isWin) return 'Gewonnen!';
if (this.isLoss) return 'Verloren!'; if (this.isLoss) return 'Verloren!';
if (this.isDraw) return 'Unentschieden!'; if (this.isDraw) return 'Unentschieden!';
return ''; return '';
} }
getResultMessage(): string { getResultMessage(): string {
if (this.isWin) return 'Glückwunsch! Du hast diese Runde gewonnen.'; if (this.isWin) return 'Glückwunsch! Du hast diese Runde gewonnen.';
if (this.isLoss) return 'Schade! Du hast diese Runde verloren.'; if (this.isLoss) return 'Schade! Du hast diese Runde verloren.';
if (this.isDraw) return 'Diese Runde endet unentschieden. Dein Einsatz wurde zurückgegeben.'; if (this.isDraw) return 'Diese Runde endet unentschieden. Dein Einsatz wurde zurückgegeben.';
return ''; return '';
} }
getResultClass(): string { getResultClass(): string {
if (this.isWin) return 'text-emerald'; if (this.isWin) return 'text-emerald';
if (this.isLoss) return 'text-accent-red'; if (this.isLoss) return 'text-accent-red';
if (this.isDraw) return 'text-yellow-400'; if (this.isDraw) return 'text-yellow-400';
return ''; return '';
} }
closeDialog(): void { closeDialog(): void {
this.visible = false; this.visible = false;
this.close.emit(); this.gameResultClosed.emit();
console.log('Dialog closed by user'); console.log('Dialog closed by user');
} }
} }

View file

@ -37,30 +37,30 @@ import { Card } from '../../models/blackjack.model';
export class PlayerHandComponent implements OnChanges { export class PlayerHandComponent implements OnChanges {
@Input() cards: Card[] = []; @Input() cards: Card[] = [];
cardsWithState: (Card & { isNew: boolean; id: string })[] = []; cardsWithState: (Card & { isNew: boolean; id: string })[] = [];
private lastCardCount = 0; private lastCardCount = 0;
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes['cards']) { if (changes['cards']) {
this.updateCardsWithState(); this.updateCardsWithState();
} }
} }
private updateCardsWithState(): void { private updateCardsWithState(): void {
const newCards = this.cards.length > this.lastCardCount; const newCards = this.cards.length > this.lastCardCount;
this.cardsWithState = this.cards.map((card, index) => { this.cardsWithState = this.cards.map((card, index) => {
// Consider a card new if it's added after the initial state and is the latest card // Consider a card new if it's added after the initial state and is the latest card
const isNew = newCards && index >= this.lastCardCount; const isNew = newCards && index >= this.lastCardCount;
return { return {
...card, ...card,
isNew, isNew,
// Generate a unique ID to help Angular track the cards // Generate a unique ID to help Angular track the cards
id: `${card.suit}-${card.rank}-${index}` id: `${card.suit}-${card.rank}-${index}`,
}; };
}); });
this.lastCardCount = this.cards.length; this.lastCardCount = this.cards.length;
} }
} }

View file

@ -1,4 +1,12 @@
import { ChangeDetectionStrategy, Component, Input, AfterViewInit, ElementRef, OnChanges, SimpleChanges } from '@angular/core'; import {
ChangeDetectionStrategy,
Component,
Input,
AfterViewInit,
ElementRef,
OnChanges,
SimpleChanges,
} from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { suitSymbols, Suit } from '../../models/blackjack.model'; import { suitSymbols, Suit } from '../../models/blackjack.model';
import { gsap } from 'gsap'; import { gsap } from 'gsap';
@ -14,7 +22,9 @@ import { gsap } from 'gsap';
[class]="hidden ? 'bg-red-800' : 'bg-white'" [class]="hidden ? 'bg-red-800' : 'bg-white'"
> >
@if (!hidden) { @if (!hidden) {
<span class="text-xl font-bold" [class]="isRedSuit ? 'text-accent-red' : 'text-black'">{{ getDisplayRank(rank) }}</span> <span class="text-xl font-bold" [class]="isRedSuit ? 'text-accent-red' : 'text-black'">{{
getDisplayRank(rank)
}}</span>
} }
@if (!hidden) { @if (!hidden) {
<span <span
@ -24,25 +34,29 @@ import { gsap } from 'gsap';
> >
} }
@if (!hidden) { @if (!hidden) {
<span class="text-xl font-bold self-end rotate-180" [class]="isRedSuit ? 'text-accent-red' : 'text-black'">{{ <span
getDisplayRank(rank) class="text-xl font-bold self-end rotate-180"
}}</span> [class]="isRedSuit ? 'text-accent-red' : 'text-black'"
>{{ getDisplayRank(rank) }}</span
>
} }
</div> </div>
`, `,
styles: [` styles: [
.card-element { `
transform-style: preserve-3d; .card-element {
backface-visibility: hidden; transform-style: preserve-3d;
} backface-visibility: hidden;
`], }
`,
],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class PlayingCardComponent implements AfterViewInit, OnChanges { export class PlayingCardComponent implements AfterViewInit, OnChanges {
@Input({ required: true }) rank!: string; @Input({ required: true }) rank!: string;
@Input({ required: true }) suit!: Suit; @Input({ required: true }) suit!: Suit;
@Input({ required: true }) hidden!: boolean; @Input({ required: true }) hidden!: boolean;
@Input() isNew: boolean = false; @Input() isNew = false;
constructor(private elementRef: ElementRef) {} constructor(private elementRef: ElementRef) {}
@ -66,31 +80,31 @@ export class PlayingCardComponent implements AfterViewInit, OnChanges {
const cardElement = this.elementRef.nativeElement.querySelector('.card-element'); const cardElement = this.elementRef.nativeElement.querySelector('.card-element');
gsap.fromTo( gsap.fromTo(
cardElement, cardElement,
{ {
y: -100, y: -100,
opacity: 0, opacity: 0,
rotation: -10, rotation: -10,
scale: 0.7 scale: 0.7,
}, },
{ {
y: 0, y: 0,
opacity: 1, opacity: 1,
rotation: 0, rotation: 0,
scale: 1, scale: 1,
duration: 0.5, duration: 0.5,
ease: 'power2.out' ease: 'power2.out',
} }
); );
} }
private animateCardFlip(): void { private animateCardFlip(): void {
const cardElement = this.elementRef.nativeElement.querySelector('.card-element'); const cardElement = this.elementRef.nativeElement.querySelector('.card-element');
gsap.to(cardElement, { gsap.to(cardElement, {
rotationY: 180, rotationY: 180,
duration: 0.3, duration: 0.3,
onComplete: () => { onComplete: () => {
gsap.set(cardElement, { rotationY: 0 }); gsap.set(cardElement, { rotationY: 0 });
} },
}); });
} }