Skip to content

Commit 051fc5b

Browse files
JavaScript and styling optimizations (#19)
* Fix indentation in index.html for improved readability * Dynamically load confetti and PDF generation libraries to improve performance and reduce initial load time * Implement dynamic loading for confetti and PDF generation libraries to enhance performance and error handling * Enhance dark theme styles for mode toggle and theme options with improved contrast * Add ResumeGameDialog component and integrate game state resumption logic for Custom and Standard Bingo
1 parent 1bc1c7d commit 051fc5b

12 files changed

Lines changed: 607 additions & 172 deletions

BingoApp/Components/Confetti.razor

Lines changed: 129 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@using Microsoft.JSInterop
12
@inject IJSRuntime JSRuntime
23

34
@code {
@@ -15,120 +16,137 @@
1516
if (Show)
1617
{
1718
await TriggerConfetti();
18-
}
19-
}
20-
21-
private async Task TriggerConfetti()
22-
{
23-
// Create an array of exciting colors
24-
var colors = new[] { "#26ccff", "#a25afd", "#ff5e7e", "#88ff5a", "#fcff42", "#ffa62d", "#ff36ff" };
25-
26-
// Calculate ticks for each phase (approximately 50 ticks per second)
27-
var totalTicks = (int)(Duration * 50);
28-
var initialBurstTicks = (int)(totalTicks * 0.3);
29-
var topWaveTicks = (int)(totalTicks * 0.2);
30-
var finaleTicks = totalTicks - initialBurstTicks - topWaveTicks;
31-
32-
// Initial burst wave
33-
await Task.WhenAll(
34-
// Center burst
35-
JSRuntime.InvokeVoidAsync("confetti", new
36-
{
37-
particleCount = ParticleCount / 2,
38-
spread = 360,
39-
startVelocity = 35,
40-
origin = new { x = 0.5, y = 0.5 },
41-
ticks = initialBurstTicks
42-
}).AsTask(),
43-
// Left burst
44-
JSRuntime.InvokeVoidAsync("confetti", new
45-
{
46-
particleCount = ParticleCount / 3,
47-
angle = 80,
48-
spread = 60,
49-
origin = new { x = 0.2, y = 0.5 },
50-
colors = colors,
51-
ticks = initialBurstTicks
52-
}).AsTask(),
53-
// Right burst
54-
JSRuntime.InvokeVoidAsync("confetti", new
55-
{
56-
particleCount = ParticleCount / 3,
57-
angle = 100,
58-
spread = 60,
59-
origin = new { x = 0.8, y = 0.5 },
60-
colors = colors,
61-
ticks = initialBurstTicks
62-
}).AsTask()
63-
);
64-
65-
66-
// Top wave bursts in parallel
67-
var topBursts = new List<Task>();
68-
for (int i = 0; i < 7; i++)
19+
} } private async Task TriggerConfetti()
20+
{ try
6921
{
70-
var x = 0.15 + (i * 0.12);
71-
var angle = 260 + (i * 3);
72-
topBursts.Add(JSRuntime.InvokeVoidAsync("confetti", new
22+
// Use the dynamic loader helper to load confetti
23+
var loadResult = await JSRuntime.InvokeAsync<LoadResult>("loadConfettiForAnimation");
24+
25+
if (!loadResult.Success)
7326
{
74-
particleCount = ParticleCount / 6,
75-
spread = 65,
76-
startVelocity = 25 + (i * 2),
77-
decay = 0.96,
78-
gravity = 0.7,
79-
drift = i - 3,
80-
angle = angle,
81-
origin = new { x = x, y = 0.0 },
82-
colors = colors,
83-
ticks = topWaveTicks
84-
}).AsTask());
85-
}
86-
await Task.WhenAll(topBursts);
27+
Console.WriteLine($"Failed to load confetti library: {loadResult.Error}");
28+
return;
29+
}
30+
31+
// Create an array of exciting colors
32+
var colors = new[] { "#26ccff", "#a25afd", "#ff5e7e", "#88ff5a", "#fcff42", "#ffa62d", "#ff36ff" };
33+
34+
// Calculate ticks for each phase (approximately 50 ticks per second)
35+
var totalTicks = (int)(Duration * 50);
36+
var initialBurstTicks = (int)(totalTicks * 0.3);
37+
var topWaveTicks = (int)(totalTicks * 0.2);
38+
var finaleTicks = totalTicks - initialBurstTicks - topWaveTicks;
8739

88-
// Grand finale
89-
await Task.WhenAll(
90-
// Center grand finale
91-
JSRuntime.InvokeVoidAsync("confetti", new
92-
{
93-
particleCount = ParticleCount,
94-
spread = 130,
95-
startVelocity = 35,
96-
decay = 0.92,
97-
gravity = 0.85,
98-
drift = 0,
99-
ticks = finaleTicks,
100-
origin = new { x = 0.5, y = 0.5 },
101-
colors = colors,
102-
shapes = new[] { "square", "circle", "star" }
103-
}).AsTask(),
104-
// Left shower
105-
JSRuntime.InvokeVoidAsync("confetti", new
40+
// Initial burst wave
41+
await Task.WhenAll(
42+
// Center burst
43+
JSRuntime.InvokeVoidAsync("confetti", new
44+
{
45+
particleCount = ParticleCount / 2,
46+
spread = 360,
47+
startVelocity = 35,
48+
origin = new { x = 0.5, y = 0.5 },
49+
ticks = initialBurstTicks
50+
}).AsTask(),
51+
// Left burst
52+
JSRuntime.InvokeVoidAsync("confetti", new
53+
{
54+
particleCount = ParticleCount / 3,
55+
angle = 80,
56+
spread = 60,
57+
origin = new { x = 0.2, y = 0.5 },
58+
colors = colors,
59+
ticks = initialBurstTicks
60+
}).AsTask(),
61+
// Right burst
62+
JSRuntime.InvokeVoidAsync("confetti", new
63+
{
64+
particleCount = ParticleCount / 3,
65+
angle = 100,
66+
spread = 60,
67+
origin = new { x = 0.8, y = 0.5 },
68+
colors = colors,
69+
ticks = initialBurstTicks
70+
}).AsTask()
71+
);
72+
73+
// Top wave bursts in parallel
74+
var topBursts = new List<Task>();
75+
for (int i = 0; i < 7; i++)
10676
{
107-
particleCount = ParticleCount / 2,
108-
angle = 80,
109-
spread = 45,
110-
startVelocity = 45,
111-
decay = 0.92,
112-
origin = new { x = 0.25, y = 0.35 },
113-
gravity = 0.8,
114-
drift = 1,
115-
colors = colors,
116-
ticks = finaleTicks
117-
}).AsTask(),
118-
// Right shower
119-
JSRuntime.InvokeVoidAsync("confetti", new
120-
{
121-
particleCount = ParticleCount / 2,
122-
angle = 100,
123-
spread = 45,
124-
startVelocity = 45,
125-
decay = 0.92,
126-
origin = new { x = 0.75, y = 0.35 },
127-
gravity = 0.8,
128-
drift = -1,
129-
colors = colors,
130-
ticks = finaleTicks
131-
}).AsTask()
132-
);
77+
var x = 0.15 + (i * 0.12);
78+
var angle = 260 + (i * 3);
79+
topBursts.Add(JSRuntime.InvokeVoidAsync("confetti", new
80+
{
81+
particleCount = ParticleCount / 6,
82+
spread = 65,
83+
startVelocity = 25 + (i * 2),
84+
decay = 0.96,
85+
gravity = 0.7,
86+
drift = i - 3,
87+
angle = angle,
88+
origin = new { x = x, y = 0.0 },
89+
colors = colors,
90+
ticks = topWaveTicks
91+
}).AsTask());
92+
}
93+
await Task.WhenAll(topBursts);
94+
95+
// Grand finale
96+
await Task.WhenAll(
97+
// Center grand finale
98+
JSRuntime.InvokeVoidAsync("confetti", new
99+
{
100+
particleCount = ParticleCount,
101+
spread = 130,
102+
startVelocity = 35,
103+
decay = 0.92,
104+
gravity = 0.85,
105+
drift = 0,
106+
ticks = finaleTicks,
107+
origin = new { x = 0.5, y = 0.5 },
108+
colors = colors,
109+
shapes = new[] { "square", "circle", "star" }
110+
}).AsTask(),
111+
// Left shower
112+
JSRuntime.InvokeVoidAsync("confetti", new
113+
{
114+
particleCount = ParticleCount / 2,
115+
angle = 80,
116+
spread = 45,
117+
startVelocity = 45,
118+
decay = 0.92,
119+
origin = new { x = 0.25, y = 0.35 },
120+
gravity = 0.8,
121+
drift = 1,
122+
colors = colors,
123+
ticks = finaleTicks
124+
}).AsTask(),
125+
// Right shower
126+
JSRuntime.InvokeVoidAsync("confetti", new
127+
{
128+
particleCount = ParticleCount / 2,
129+
angle = 100,
130+
spread = 45,
131+
startVelocity = 45,
132+
decay = 0.92,
133+
origin = new { x = 0.75, y = 0.35 },
134+
gravity = 0.8,
135+
drift = -1,
136+
colors = colors,
137+
ticks = finaleTicks
138+
}).AsTask()
139+
);
140+
} catch (Exception ex)
141+
{
142+
// Gracefully handle confetti loading failures
143+
Console.WriteLine($"Failed to load or execute confetti animation: {ex.Message}");
144+
}
145+
}
146+
147+
private class LoadResult
148+
{
149+
public bool Success { get; set; }
150+
public string? Error { get; set; }
133151
}
134152
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
@using Microsoft.AspNetCore.Components
2+
3+
<div class="modal-backdrop @(Show ? "show" : "")" style="display: @(Show ? "block" : "none")"></div>
4+
<div class="modal fade @(Show ? "show" : "")" tabindex="-1" role="dialog" style="display: @(Show ? "block" : "none")">
5+
<div class="modal-dialog modal-dialog-centered" role="document">
6+
<div class="modal-content">
7+
<div class="modal-header">
8+
<h5 class="modal-title">
9+
<i class="bi bi-clock-history me-2"></i>
10+
Previous Game Found
11+
</h5>
12+
</div>
13+
<div class="modal-body">
14+
<div class="text-center mb-3">
15+
<i class="bi bi-question-circle-fill text-primary" style="font-size: 3rem;"></i>
16+
</div>
17+
<p class="text-center mb-3">
18+
We found a previous @GameType game that was in progress.
19+
Would you like to continue where you left off?
20+
</p>
21+
@if (!string.IsNullOrEmpty(GameDetails))
22+
{
23+
<div class="alert alert-info">
24+
<small>@GameDetails</small>
25+
</div>
26+
}
27+
</div>
28+
<div class="modal-footer">
29+
<button type="button" class="btn btn-outline-secondary" @onclick="OnNoClicked">
30+
<i class="bi bi-x-circle me-1"></i>
31+
Start New Game
32+
</button>
33+
<button type="button" class="btn btn-primary" @onclick="OnYesClicked">
34+
<i class="bi bi-play-circle me-1"></i>
35+
Resume Game
36+
</button>
37+
</div>
38+
</div>
39+
</div>
40+
</div>
41+
42+
@code {
43+
[Parameter] public bool Show { get; set; }
44+
[Parameter] public string GameType { get; set; } = "Bingo";
45+
[Parameter] public string? GameDetails { get; set; }
46+
[Parameter] public EventCallback<bool> OnResult { get; set; }
47+
48+
private async Task OnYesClicked()
49+
{
50+
await OnResult.InvokeAsync(true);
51+
}
52+
53+
private async Task OnNoClicked()
54+
{
55+
await OnResult.InvokeAsync(false);
56+
}
57+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
.modal-backdrop.show {
2+
opacity: 0.5;
3+
}
4+
5+
.modal.show {
6+
display: block !important;
7+
}
8+
9+
.modal-content {
10+
border: none;
11+
border-radius: 12px;
12+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
13+
}
14+
15+
.modal-header {
16+
border-bottom: 1px solid var(--bs-border-color);
17+
padding: 1.5rem;
18+
background: linear-gradient(135deg, var(--bs-primary) 0%, var(--bs-info) 100%);
19+
color: white;
20+
border-radius: 12px 12px 0 0;
21+
}
22+
23+
.modal-title {
24+
font-weight: 600;
25+
margin: 0;
26+
}
27+
28+
.modal-body {
29+
padding: 2rem 1.5rem;
30+
}
31+
32+
.modal-footer {
33+
border-top: 1px solid var(--bs-border-color);
34+
padding: 1.5rem;
35+
gap: 0.75rem;
36+
}
37+
38+
.modal-footer .btn {
39+
min-width: 140px;
40+
font-weight: 500;
41+
}
42+
43+
.modal-footer .btn-outline-secondary {
44+
border-color: var(--bs-gray-400);
45+
}
46+
47+
.modal-footer .btn-outline-secondary:hover {
48+
background-color: var(--bs-gray-500);
49+
border-color: var(--bs-gray-500);
50+
}
51+
52+
.modal-footer .btn-primary {
53+
background: linear-gradient(135deg, var(--bs-primary) 0%, var(--bs-info) 100%);
54+
border: none;
55+
box-shadow: 0 2px 8px rgba(var(--bs-primary-rgb), 0.3);
56+
}
57+
58+
.modal-footer .btn-primary:hover {
59+
transform: translateY(-1px);
60+
box-shadow: 0 4px 12px rgba(var(--bs-primary-rgb), 0.4);
61+
}
62+
63+
.alert-info {
64+
background-color: rgba(var(--bs-info-rgb), 0.1);
65+
border-color: rgba(var(--bs-info-rgb), 0.2);
66+
color: var(--bs-info-text);
67+
}
68+
69+
@media (max-width: 576px) {
70+
.modal-dialog {
71+
margin: 1rem;
72+
}
73+
74+
.modal-footer {
75+
flex-direction: column;
76+
}
77+
78+
.modal-footer .btn {
79+
width: 100%;
80+
}
81+
}

0 commit comments

Comments
 (0)