// Copyright (c) MudBlazor 2021 // MudBlazor licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Components.Web; using Microsoft.JSInterop; namespace Connected.Services; /// /// This transient service binds itself to a parent element to observe the keys of one of its children. /// It can call preventDefault or stopPropagation directly on the JavaScript side for single key strokes / key combinations as per configuration. /// Furthermore, you can precisely subscribe single keystrokes or combinations and only the subscribed ones will be forwarded into .NET /// public class KeyInterceptor : IKeyInterceptor, IDisposable { private bool _isDisposed = false; private readonly DotNetObjectReference _dotNetRef; private readonly IJSRuntime _jsRuntime; private bool _isObserving; private string _elementId; [DynamicDependency(nameof(OnKeyDown))] [DynamicDependency(nameof(OnKeyUp))] [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(KeyboardEvent))] [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(KeyboardEventArgs))] [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(KeyOptions))] [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(KeyInterceptorOptions))] public KeyInterceptor(IJSRuntime jsRuntime) { _dotNetRef = DotNetObjectReference.Create(this); _jsRuntime = jsRuntime; } /// /// Connect to the ancestor element of the element(s) that should be observed /// /// Ancestor html element id /// Define here the descendant(s) by setting TargetClass and the keystrokes to be monitored / suppressed public async Task Connect(string elementId, KeyInterceptorOptions options) { if (_isObserving || _isDisposed) return; _elementId = elementId; _isObserving = await _jsRuntime.InvokeVoidAsyncWithErrorHandling("mudKeyInterceptor.connect", _dotNetRef, elementId, options); } /// /// Update behavior of a registered keyoption /// The keystrike to update has to be monitored previously /// /// Define KeyOption to update public async Task UpdateKey(KeyOptions option) { await _jsRuntime.InvokeVoidAsync($"mudKeyInterceptor.updatekey", _elementId, option); } /// /// Disconnect from the previously connected ancestor and its descendants /// public async Task Disconnect() { try { await _jsRuntime.InvokeVoidAsync($"mudKeyInterceptor.disconnect", _elementId); } catch (Exception) { /*ignore*/ } _isObserving = false; } [JSInvokable] public void OnKeyDown(KeyboardEventArgs args) { KeyDown?.Invoke(args); } [JSInvokable] public void OnKeyUp(KeyboardEventArgs args) { KeyUp?.Invoke(args); } public event KeyboardEvent KeyDown; public event KeyboardEvent KeyUp; protected virtual void Dispose(bool disposing) { if (!disposing || _isDisposed) return; _isDisposed = true; KeyDown = null; KeyUp = null; Disconnect().AndForget(); _dotNetRef.Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }