diff --git a/src/GoodWood.Web/Components/Game/NumberPanel.razor b/src/GoodWood.Web/Components/Game/NumberPanel.razor
index 2e0a2cc..46c451d 100644
--- a/src/GoodWood.Web/Components/Game/NumberPanel.razor
+++ b/src/GoodWood.Web/Components/Game/NumberPanel.razor
@@ -1,4 +1,7 @@
@using GoodWood.Domain.Enums
+@using Microsoft.JSInterop
+@inject IJSRuntime JS
+@implements IAsyncDisposable
@@ -132,6 +135,41 @@
// };
// }
+ private DotNetObjectReference? _dotNetRef;
+ private IJSObjectReference? _jsModule;
+ private IJSObjectReference? _keyListener;
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ if (firstRender)
+ {
+ _dotNetRef = DotNetObjectReference.Create(this);
+ _jsModule = await JS.InvokeAsync("import", "/js/numberPanel.js");
+ _keyListener = await _jsModule.InvokeAsync("registerKeyListener", _dotNetRef);
+ }
+ }
+
+ [JSInvokable]
+ public async Task HandleKeyPress(int number)
+ {
+ if (!IsInteractive)
+ return;
+
+ await OnNumberClicked.InvokeAsync(number);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ if (_keyListener is not null)
+ {
+ await _keyListener.InvokeVoidAsync("dispose");
+ await _keyListener.DisposeAsync();
+ }
+ if (_jsModule is not null)
+ await _jsModule.DisposeAsync();
+ _dotNetRef?.Dispose();
+ }
+
private async Task HandleNumberClick(int number)
{
if (!IsInteractive)
diff --git a/src/GoodWood.Web/wwwroot/js/numberPanel.js b/src/GoodWood.Web/wwwroot/js/numberPanel.js
new file mode 100644
index 0000000..5ddfc22
--- /dev/null
+++ b/src/GoodWood.Web/wwwroot/js/numberPanel.js
@@ -0,0 +1,9 @@
+export function registerKeyListener(dotNetRef) {
+ function handler(e) {
+ if (e.key >= '0' && e.key <= '9' && !e.ctrlKey && !e.altKey && !e.metaKey) {
+ dotNetRef.invokeMethodAsync('HandleKeyPress', parseInt(e.key));
+ }
+ }
+ document.addEventListener('keydown', handler);
+ return { dispose: () => document.removeEventListener('keydown', handler) };
+}