Skip to content

Commit 9ba1f0b

Browse files
committed
updated
1 parent ac18298 commit 9ba1f0b

10 files changed

Lines changed: 889 additions & 60 deletions

File tree

Wordle/Components/Modal.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
@if (IsOpen)
22
{
33
<div class="modal-overlay" @onclick="OnBackdropClick">
4-
<div class="absolute inset-0 bg-black/50" aria-hidden="true"></div>
4+
<div class="modal-backdrop" aria-hidden="true"></div>
55
<div class="modal-panel" @onclick:stopPropagation="true">
66
@if (!string.IsNullOrEmpty(Title))
77
{

Wordle/Components/Settings.razor

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
@namespace Wordle.Components
2+
3+
<div class="settings-content" style="color: var(--outline-dark);">
4+
<div class="settings-group">
5+
<p class="settings-label">Theme</p>
6+
<div class="settings-row">
7+
<label class="settings-option">
8+
<input type="radio" name="theme" checked="@(Theme == "light")" @onchange="@(() => SetTheme("light"))" />
9+
<span>Light</span>
10+
</label>
11+
<label class="settings-option">
12+
<input type="radio" name="theme" checked="@(Theme == "dark")" @onchange="@(() => SetTheme("dark"))" />
13+
<span>Dark</span>
14+
</label>
15+
</div>
16+
</div>
17+
<div class="settings-group">
18+
<p class="settings-label">Color palette</p>
19+
<div class="settings-row settings-row-wrap">
20+
<label class="settings-option">
21+
<input type="radio" name="palette" checked="@(Palette == "default")" @onchange="@(() => SetPalette("default"))" />
22+
<span>Default</span>
23+
</label>
24+
<label class="settings-option">
25+
<input type="radio" name="palette" checked="@(Palette == "highcontrast")" @onchange="@(() => SetPalette("highcontrast"))" />
26+
<span>High contrast</span>
27+
</label>
28+
<label class="settings-option">
29+
<input type="radio" name="palette" checked="@(Palette == "retro")" @onchange="@(() => SetPalette("retro"))" />
30+
<span>Retro</span>
31+
</label>
32+
<label class="settings-option">
33+
<input type="radio" name="palette" checked="@(Palette == "neon")" @onchange="@(() => SetPalette("neon"))" />
34+
<span>Neon</span>
35+
</label>
36+
</div>
37+
</div>
38+
<div class="settings-group">
39+
<label class="settings-toggle">
40+
<input type="checkbox" checked="@HardMode" @onchange="@(e => PersistHardMode(e))" />
41+
<span>Hard mode (use revealed letters)</span>
42+
</label>
43+
</div>
44+
<div class="settings-group">
45+
<label class="settings-toggle">
46+
<input type="checkbox" checked="@SoundOn" @onchange="@(e => PersistSound(e))" />
47+
<span>Sound effects</span>
48+
</label>
49+
</div>
50+
<div class="settings-group">
51+
<label class="settings-toggle">
52+
<input type="checkbox" checked="@HapticsOn" @onchange="@(e => PersistHaptics(e))" />
53+
<span>Haptic feedback</span>
54+
</label>
55+
</div>
56+
<div class="settings-group">
57+
<label class="settings-toggle">
58+
<input type="checkbox" checked="@ReduceMotion" @onchange="@(e => PersistReduceMotion(e))" />
59+
<span>Reduce motion</span>
60+
</label>
61+
</div>
62+
<button type="button" class="wordle-btn-primary w-full mt-4 py-2 px-4 rounded font-semibold" @onclick="Close">Close</button>
63+
</div>
64+
65+
@code {
66+
[Parameter] public string Theme { get; set; } = "light";
67+
[Parameter] public string Palette { get; set; } = "default";
68+
[Parameter] public bool HardMode { get; set; }
69+
[Parameter] public bool SoundOn { get; set; }
70+
[Parameter] public bool HapticsOn { get; set; }
71+
[Parameter] public bool ReduceMotion { get; set; }
72+
[Parameter] public EventCallback OnClose { get; set; }
73+
[Parameter] public EventCallback OnThemeChange { get; set; }
74+
[Parameter] public EventCallback OnPaletteChange { get; set; }
75+
[Parameter] public EventCallback OnHardModeChange { get; set; }
76+
[Parameter] public EventCallback OnSoundChange { get; set; }
77+
[Parameter] public EventCallback OnHapticsChange { get; set; }
78+
[Parameter] public EventCallback OnReduceMotionChange { get; set; }
79+
80+
[Inject] private IJSRuntime JSRuntime { get; set; } = null!;
81+
82+
private async Task SetTheme(string theme)
83+
{
84+
await JSRuntime.InvokeVoidAsync("setWordleTheme", theme);
85+
await OnThemeChange.InvokeAsync();
86+
}
87+
88+
private async Task SetPalette(string palette)
89+
{
90+
await JSRuntime.InvokeVoidAsync("setWordlePalette", palette);
91+
await OnPaletteChange.InvokeAsync();
92+
}
93+
94+
private async Task PersistHardMode(ChangeEventArgs e)
95+
{
96+
var v = (e.Value as bool?) ?? false;
97+
await JSRuntime.InvokeVoidAsync("setWordleHardMode", v);
98+
await OnHardModeChange.InvokeAsync();
99+
}
100+
101+
private async Task PersistSound(ChangeEventArgs e)
102+
{
103+
var v = (e.Value as bool?) ?? false;
104+
await JSRuntime.InvokeVoidAsync("setWordleSound", v);
105+
await OnSoundChange.InvokeAsync();
106+
}
107+
108+
private async Task PersistHaptics(ChangeEventArgs e)
109+
{
110+
var v = (e.Value as bool?) ?? false;
111+
await JSRuntime.InvokeVoidAsync("setWordleHaptics", v);
112+
await OnHapticsChange.InvokeAsync();
113+
}
114+
115+
private async Task PersistReduceMotion(ChangeEventArgs e)
116+
{
117+
var v = (e.Value as bool?) ?? false;
118+
await JSRuntime.InvokeVoidAsync("setWordleReduceMotion", v);
119+
await OnReduceMotionChange.InvokeAsync();
120+
}
121+
122+
private async Task Close() => await OnClose.InvokeAsync();
123+
}

Wordle/Components/Sliding.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div style="opacity:40%">
1+
<div class="wordle-sliding-bg" style="opacity:40%">
22

33
<div class="circle0">
44
</div>

Wordle/Model/Cell.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,15 @@ public string GetTextColor()
2626
if (Config.CurrentIndex == Row) return Config.Theme == "dark" ? "#e8e0d0" : "#2c2420";
2727
return "#ffffff";
2828
}
29+
30+
/// <summary>Returns CSS class for revealed tile state so palette variables apply; empty when empty or current row.</summary>
31+
public string GetStateClass()
32+
{
33+
if (PlaceHolder == ' ' || Config.CurrentIndex == Row) return "";
34+
var word = Config.WordToFind;
35+
if (word[Col] == PlaceHolder) return "wordle-tile-state-correct";
36+
if (word.Contains(PlaceHolder)) return "wordle-tile-state-present";
37+
return "wordle-tile-state-absent";
38+
}
2939
}
3040
}

Wordle/Model/Config.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ public static string GetKeyBoardColor(int i)
2020
};
2121
}
2222

23+
/// <summary>Returns CSS class for key state so palette variables apply; empty for default.</summary>
24+
public static string GetKeyBoardClass(int i)
25+
{
26+
return i switch { 0 => "wordle-key-absent", 1 => "wordle-key-present", 2 => "wordle-key-correct", _ => "" };
27+
}
28+
2329
public static void Reset()
2430
{
2531
List = [];

Wordle/Pages/Index.razor

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
@page "/"
22
<Sliding />
33
<div class="wordle-app">
4-
<header class="wordle-header">
4+
<div id="wordle-announcer" class="wordle-sr-only" aria-live="polite" aria-atomic="true">@announcementText</div>
5+
<header class="wordle-header @(isDailyMode && !string.IsNullOrEmpty(nextWordIn) ? "wordle-header-with-countdown" : "")">
56
<h1 class="wordle-title">WORDLE</h1>
7+
@if (isDailyMode && !string.IsNullOrEmpty(nextWordIn))
8+
{
9+
<p class="wordle-next-word-in" aria-live="polite">Next word in @nextWordIn</p>
10+
}
611
<div class="wordle-header-actions">
712
<label class="wordle-daily-toggle" title="Daily word (same word for everyone today)">
813
<input type="checkbox" checked="@isDailyMode" @onchange="@(e => ToggleDailyMode(e))" />
914
<span>Daily</span>
1015
</label>
16+
@if (isHardMode)
17+
{
18+
<span class="wordle-hard-badge" title="Hard mode: use revealed letters">Hard</span>
19+
}
1120
<button type="button" class="wordle-icon-btn" aria-label="@(theme == "dark" ? "Switch to light mode" : "Switch to dark mode")" @onclick="ToggleThemeAsync" title="@(theme == "dark" ? "Light mode" : "Dark mode")">@(theme == "dark" ? "☀️" : "🌙")</button>
1221
<button type="button" class="wordle-icon-btn" aria-label="Statistics" @onclick="OpenStatsModalAsync">📊</button>
1322
<button type="button" class="wordle-icon-btn" aria-label="How to play" @onclick="HelpDialog">?</button>
23+
<button type="button" class="wordle-icon-btn" aria-label="Settings" @onclick="OpenSettingsModalAsync">⚙️</button>
1424
</div>
1525
</header>
1626
<main class="wordle-main">
@@ -22,8 +32,8 @@
2232
@for (var colIndex = 0; colIndex < row.Count; colIndex++)
2333
{
2434
var point = row[colIndex];
25-
<div class="@TileCssClass(rowIndex, colIndex)" style="background-color:@point.GetBackGroundColor(); border-color:@(point.PlaceHolder == ' ' || Config.CurrentIndex == point.Row ? BorderColor : "transparent");">
26-
<span class="wordle-tile-letter" style="color:@point.GetTextColor();">@point.PlaceHolderToString()</span>
35+
<div class="@TileCssClass(rowIndex, colIndex) @point.GetStateClass()" style="@(string.IsNullOrEmpty(point.GetStateClass()) ? "background-color:" + point.GetBackGroundColor() + "; color:" + point.GetTextColor() + "; " : "")border-color:@(point.PlaceHolder == ' ' || Config.CurrentIndex == point.Row ? BorderColor : "transparent");">
36+
<span class="wordle-tile-letter" style="@(string.IsNullOrEmpty(point.GetStateClass()) ? "color:" + point.GetTextColor() : "");">@point.PlaceHolderToString()</span>
2737
</div>
2838
}
2939
</div>
@@ -33,22 +43,25 @@
3343
<div class="wordle-keyrow">
3444
@foreach (var item in alphabet[0])
3545
{
36-
<button type="button" class="wordle-key" style="@(hashOfCharacter.ContainsKey(item) ? Config.GetKeyBoardColor(hashOfCharacter[item]) : KeyStyleDefault)" @onclick='() => OnArrowKeyPressed(item.ToString())'>@item.ToString().ToUpper()</button>
46+
var keyClass = hashOfCharacter.ContainsKey(item) ? Config.GetKeyBoardClass(hashOfCharacter[item]) : "";
47+
<button type="button" class="wordle-key @keyClass" style="@(string.IsNullOrEmpty(keyClass) ? KeyStyleDefault : "")" @onclick='() => OnArrowKeyPressed(item.ToString())'>@item.ToString().ToUpper()</button>
3748
}
3849
</div>
3950
<div class="wordle-keyrow">
4051
<div class="wordle-key-spacer"></div>
4152
@foreach (var item in alphabet[1])
4253
{
43-
<button type="button" class="wordle-key" style="@(hashOfCharacter.ContainsKey(item) ? Config.GetKeyBoardColor(hashOfCharacter[item]) : KeyStyleDefault)" @onclick='() => OnArrowKeyPressed(item.ToString())'>@item.ToString().ToUpper()</button>
54+
var keyClass1 = hashOfCharacter.ContainsKey(item) ? Config.GetKeyBoardClass(hashOfCharacter[item]) : "";
55+
<button type="button" class="wordle-key @keyClass1" style="@(string.IsNullOrEmpty(keyClass1) ? KeyStyleDefault : "")" @onclick='() => OnArrowKeyPressed(item.ToString())'>@item.ToString().ToUpper()</button>
4456
}
4557
<div class="wordle-key-spacer"></div>
4658
</div>
4759
<div class="wordle-keyrow">
4860
<button type="button" class="wordle-key wordle-key-wide" style="@KeyStyleDefault" @onclick='() => OnArrowKeyPressed("Enter")'>ENTER</button>
4961
@foreach (var item in alphabet[2])
5062
{
51-
<button type="button" class="wordle-key" style="@(hashOfCharacter.ContainsKey(item) ? Config.GetKeyBoardColor(hashOfCharacter[item]) : KeyStyleDefault)" @onclick='() => OnArrowKeyPressed(item.ToString())'>@item.ToString().ToUpper()</button>
63+
var keyClass2 = hashOfCharacter.ContainsKey(item) ? Config.GetKeyBoardClass(hashOfCharacter[item]) : "";
64+
<button type="button" class="wordle-key @keyClass2" style="@(string.IsNullOrEmpty(keyClass2) ? KeyStyleDefault : "")" @onclick='() => OnArrowKeyPressed(item.ToString())'>@item.ToString().ToUpper()</button>
5265
}
5366
<button type="button" class="wordle-key wordle-key-wide" style="@KeyStyleDefault" @onclick='() => OnArrowKeyPressed("Backspace")'>⌫</button>
5467
</div>
@@ -84,6 +97,14 @@
8497
</Modal>
8598
}
8699

100+
@if (showSettingsModal)
101+
{
102+
<Modal IsOpen="true" Title="SETTINGS" OnClose="CloseSettingsModal">
103+
<Settings Theme="@theme" Palette="@palette" HardMode="@isHardMode" SoundOn="@soundOn" HapticsOn="@hapticsOn" ReduceMotion="@reduceMotion"
104+
OnClose="CloseSettingsModal" OnThemeChange="RefreshThemeAsync" OnPaletteChange="RefreshPaletteAsync"
105+
OnHardModeChange="RefreshHardModeAsync" OnSoundChange="RefreshSoundAsync" OnHapticsChange="RefreshHapticsAsync" OnReduceMotionChange="RefreshReduceMotionAsync" />
106+
</Modal>
107+
}
87108
@if (showStatsModal)
88109
{
89110
<Modal IsOpen="true" Title="STATS" OnClose="CloseStatsModal">
@@ -127,14 +148,16 @@
127148
}
128149

129150
@code {
130-
private void CloseHowToPlayModal()
151+
private async Task CloseHowToPlayModal()
131152
{
153+
try { await JSRuntime.InvokeVoidAsync("wordleRestoreFocus"); } catch { }
132154
showHowToPlayModal = false;
133155
StateHasChanged();
134156
}
135157

136-
private void CloseWinModal()
158+
private async Task CloseWinModal()
137159
{
160+
try { await JSRuntime.InvokeVoidAsync("wordleRestoreFocus"); } catch { }
138161
showWinModal = false;
139162
StateHasChanged();
140163
}

0 commit comments

Comments
 (0)