Skip to content

Commit efad31d

Browse files
committed
SnackBar Added
1 parent 8a92689 commit efad31d

File tree

4 files changed

+308
-3
lines changed

4 files changed

+308
-3
lines changed

Component/Snackbar.razor

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
@using Microsoft.JSInterop
2+
@using TomAndJerry.Component
3+
@inject IJSRuntime JSRuntime
4+
5+
<div class="snackbar-container" id="snackbar-container">
6+
@if (IsVisible)
7+
{
8+
<div class="snackbar @GetSnackbarClass()" id="snackbar">
9+
<div class="snackbar-content">
10+
<div class="snackbar-icon">
11+
@if (!string.IsNullOrEmpty(Icon))
12+
{
13+
<span class="text-2xl">@Icon</span>
14+
}
15+
</div>
16+
<div class="snackbar-message">
17+
<div class="snackbar-title">@Title</div>
18+
<div class="snackbar-text">@Message</div>
19+
</div>
20+
<button class="snackbar-close" @onclick="Hide">
21+
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
22+
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
23+
</svg>
24+
</button>
25+
</div>
26+
</div>
27+
}
28+
</div>
29+
30+
@code {
31+
[Parameter] public string Title { get; set; } = "";
32+
[Parameter] public string Message { get; set; } = "";
33+
[Parameter] public string Icon { get; set; } = "";
34+
[Parameter] public SnackbarType Type { get; set; } = SnackbarType.Info;
35+
[Parameter] public int Duration { get; set; } = 5000; // 5 seconds
36+
[Parameter] public EventCallback OnClose { get; set; }
37+
38+
private bool IsVisible { get; set; } = false;
39+
private Timer? _timer;
40+
41+
public async Task ShowAsync(string title, string message, string icon = "", SnackbarType type = SnackbarType.Info, int duration = 5000)
42+
{
43+
Title = title;
44+
Message = message;
45+
Icon = icon;
46+
Type = type;
47+
Duration = duration;
48+
IsVisible = true;
49+
50+
StateHasChanged();
51+
52+
// Auto-hide after duration
53+
if (Duration > 0)
54+
{
55+
_timer?.Dispose();
56+
_timer = new Timer(async _ => await InvokeAsync(Hide), null, Duration, Timeout.Infinite);
57+
}
58+
59+
// Trigger slide-in animation
60+
await Task.Delay(50);
61+
await JSRuntime.InvokeVoidAsync("showSnackbar");
62+
}
63+
64+
public async Task Hide()
65+
{
66+
if (!IsVisible) return;
67+
68+
await JSRuntime.InvokeVoidAsync("hideSnackbar");
69+
70+
await Task.Delay(300); // Wait for animation to complete
71+
IsVisible = false;
72+
_timer?.Dispose();
73+
_timer = null;
74+
75+
StateHasChanged();
76+
await OnClose.InvokeAsync();
77+
}
78+
79+
private string GetSnackbarClass()
80+
{
81+
return Type switch
82+
{
83+
SnackbarType.Success => "snackbar-success",
84+
SnackbarType.Error => "snackbar-error",
85+
SnackbarType.Warning => "snackbar-warning",
86+
SnackbarType.Info => "snackbar-info",
87+
_ => "snackbar-info"
88+
};
89+
}
90+
91+
public void Dispose()
92+
{
93+
_timer?.Dispose();
94+
}
95+
}

Component/SnackbarType.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace TomAndJerry.Component
2+
{
3+
public enum SnackbarType
4+
{
5+
Info,
6+
Success,
7+
Warning,
8+
Error
9+
}
10+
}

Pages/Home.razor

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@page "/"
22
@using TomAndJerry.Services
33
@using TomAndJerry.Model
4+
@using TomAndJerry.Component
45
@inject NavigationManager nav
56
@inject IVideoService VideoService
67
@inject IStateService StateService
@@ -375,12 +376,15 @@
375376
</div>
376377
</div>
377378

379+
<!-- Snackbar Component -->
380+
<Snackbar @ref="snackbar" />
378381

379382
@code
380383
{
381384
private string heroSearchTerm = string.Empty;
382385
private string activeFilter = "All";
383386
private IEnumerable<Video> filteredVideos = Enumerable.Empty<Video>();
387+
private Snackbar snackbar = new();
384388

385389
public void GoTOPage(Video video)
386390
{
@@ -438,7 +442,7 @@
438442
private async Task OnStickerSelected(Sticker sticker)
439443
{
440444
// Handle sticker selection - could show a modal, navigate, or perform an action
441-
await JSRuntime.InvokeVoidAsync("alert", $"You selected: {sticker.DisplayName}!");
445+
await snackbar.ShowAsync("Sticker Selected!", $"You selected: {sticker.DisplayName}!", "🎭", SnackbarType.Success, 4000);
442446
}
443447

444448
private async Task ShowCharacterInfo(string character)
@@ -447,7 +451,8 @@
447451
? "Tom is a blue-grey cat who lives in the house with his owner. Despite his best efforts, he can never catch Jerry, but that doesn't stop him from trying! Tom is known for his elaborate schemes and comedic failures."
448452
: "Jerry is a small brown mouse who lives in the house. He's incredibly clever and always manages to outsmart Tom with his quick thinking and resourcefulness. Jerry loves cheese and enjoys playing tricks on Tom.";
449453

450-
await JSRuntime.InvokeVoidAsync("alert", $"{character} Info:\n\n{info}");
454+
string icon = character == "Tom" ? "🐱" : "🐭";
455+
await snackbar.ShowAsync($"{character} Info", info, icon, SnackbarType.Info, 8000);
451456
}
452457

453458
private async Task ShowRandomFact()
@@ -468,7 +473,7 @@
468473
var random = new Random();
469474
var fact = facts[random.Next(facts.Length)];
470475

471-
await JSRuntime.InvokeVoidAsync("alert", $"🎭 Fun Fact:\n\n{fact}");
476+
await snackbar.ShowAsync("🎭 Fun Fact", fact, "💡", SnackbarType.Info, 6000);
472477
}
473478

474479
private async Task RefreshFeaturedEpisodes()

wwwroot/app.css

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,4 +1077,199 @@ input:focus {
10771077
padding: 0.5rem 0.75rem;
10781078
font-size: 0.8rem;
10791079
}
1080+
}
1081+
1082+
/* Snackbar Styles */
1083+
.snackbar-container {
1084+
position: fixed;
1085+
top: 20px;
1086+
right: 20px;
1087+
z-index: 9999;
1088+
pointer-events: none;
1089+
}
1090+
1091+
.snackbar {
1092+
background: rgba(255, 255, 255, 0.95);
1093+
backdrop-filter: blur(12px);
1094+
border-radius: 12px;
1095+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
1096+
border: 1px solid rgba(255, 255, 255, 0.2);
1097+
max-width: 400px;
1098+
min-width: 300px;
1099+
transform: translateX(100%);
1100+
opacity: 0;
1101+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1102+
pointer-events: auto;
1103+
margin-bottom: 12px;
1104+
}
1105+
1106+
.snackbar.show {
1107+
transform: translateX(0);
1108+
opacity: 1;
1109+
}
1110+
1111+
.snackbar-content {
1112+
display: flex;
1113+
align-items: flex-start;
1114+
padding: 16px;
1115+
gap: 12px;
1116+
}
1117+
1118+
.snackbar-icon {
1119+
flex-shrink: 0;
1120+
margin-top: 2px;
1121+
}
1122+
1123+
.snackbar-message {
1124+
flex: 1;
1125+
min-width: 0;
1126+
}
1127+
1128+
.snackbar-title {
1129+
font-weight: 600;
1130+
font-size: 14px;
1131+
line-height: 1.4;
1132+
margin-bottom: 4px;
1133+
font-family: 'Comic Sans MS', cursive;
1134+
}
1135+
1136+
.snackbar-text {
1137+
font-size: 13px;
1138+
line-height: 1.4;
1139+
color: #6b7280;
1140+
font-family: 'Comic Sans MS', cursive;
1141+
}
1142+
1143+
.snackbar-close {
1144+
flex-shrink: 0;
1145+
background: none;
1146+
border: none;
1147+
color: #9ca3af;
1148+
cursor: pointer;
1149+
padding: 4px;
1150+
border-radius: 4px;
1151+
transition: all 0.2s ease;
1152+
display: flex;
1153+
align-items: center;
1154+
justify-content: center;
1155+
}
1156+
1157+
.snackbar-close:hover {
1158+
color: #6b7280;
1159+
background: rgba(0, 0, 0, 0.05);
1160+
}
1161+
1162+
/* Snackbar Type Variants */
1163+
.snackbar-info {
1164+
border-left: 4px solid #3b82f6;
1165+
}
1166+
1167+
.snackbar-info .snackbar-icon {
1168+
color: #3b82f6;
1169+
}
1170+
1171+
.snackbar-success {
1172+
border-left: 4px solid #10b981;
1173+
}
1174+
1175+
.snackbar-success .snackbar-icon {
1176+
color: #10b981;
1177+
}
1178+
1179+
.snackbar-warning {
1180+
border-left: 4px solid #f59e0b;
1181+
}
1182+
1183+
.snackbar-warning .snackbar-icon {
1184+
color: #f59e0b;
1185+
}
1186+
1187+
.snackbar-error {
1188+
border-left: 4px solid #ef4444;
1189+
}
1190+
1191+
.snackbar-error .snackbar-icon {
1192+
color: #ef4444;
1193+
}
1194+
1195+
/* Tom & Jerry themed snackbar */
1196+
.snackbar-tom-jerry {
1197+
background: linear-gradient(135deg, rgba(96, 165, 250, 0.1) 0%, rgba(248, 113, 113, 0.1) 100%);
1198+
border: 2px solid #60A5FA;
1199+
box-shadow: 0 8px 32px rgba(96, 165, 250, 0.2);
1200+
}
1201+
1202+
.snackbar-tom-jerry .snackbar-title {
1203+
color: #1e293b;
1204+
font-weight: 700;
1205+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
1206+
}
1207+
1208+
.snackbar-tom-jerry .snackbar-text {
1209+
color: #334155;
1210+
font-weight: 500;
1211+
}
1212+
1213+
.snackbar-tom-jerry .snackbar-icon {
1214+
color: #60A5FA;
1215+
filter: drop-shadow(0 2px 4px rgba(96, 165, 250, 0.3));
1216+
}
1217+
1218+
/* Mobile Responsive */
1219+
@media (max-width: 640px) {
1220+
.snackbar-container {
1221+
top: 16px;
1222+
right: 16px;
1223+
left: 16px;
1224+
}
1225+
1226+
.snackbar {
1227+
max-width: none;
1228+
min-width: auto;
1229+
margin-bottom: 8px;
1230+
}
1231+
1232+
.snackbar-content {
1233+
padding: 14px;
1234+
gap: 10px;
1235+
}
1236+
1237+
.snackbar-title {
1238+
font-size: 13px;
1239+
}
1240+
1241+
.snackbar-text {
1242+
font-size: 12px;
1243+
}
1244+
}
1245+
1246+
/* Animation keyframes */
1247+
@keyframes snackbarSlideIn {
1248+
from {
1249+
transform: translateX(100%);
1250+
opacity: 0;
1251+
}
1252+
to {
1253+
transform: translateX(0);
1254+
opacity: 1;
1255+
}
1256+
}
1257+
1258+
@keyframes snackbarSlideOut {
1259+
from {
1260+
transform: translateX(0);
1261+
opacity: 1;
1262+
}
1263+
to {
1264+
transform: translateX(100%);
1265+
opacity: 0;
1266+
}
1267+
}
1268+
1269+
.snackbar.slide-in {
1270+
animation: snackbarSlideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
1271+
}
1272+
1273+
.snackbar.slide-out {
1274+
animation: snackbarSlideOut 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
10801275
}

0 commit comments

Comments
 (0)