using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using AntDesign.Core.Extensions; using AntDesign.Core.JsInterop.ObservableApi; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; namespace AntDesign.JsInterop { public class DomEventService { private ConcurrentDictionary> _domEventListeners = new ConcurrentDictionary>(); private readonly IJSRuntime _jsRuntime; private bool? _isResizeObserverSupported = null; public DomEventService(IJSRuntime jsRuntime) { _jsRuntime = jsRuntime; } private void AddEventListenerToFirstChildInternal(object dom, string eventName, bool preventDefault, Action callback) { if (!_domEventListeners.ContainsKey(FormatKey(dom, eventName))) { _jsRuntime.InvokeAsync(JSInteropConstants.AddDomEventListenerToFirstChild, dom, eventName, preventDefault, DotNetObjectReference.Create(new Invoker((p) => { callback?.Invoke(p); }))); } } public void AddEventListener(object dom, string eventName, Action callback, bool exclusive = true, bool preventDefault = false) { AddEventListener(dom, eventName, callback, exclusive, preventDefault); } public virtual void AddEventListener(object dom, string eventName, Action callback, bool exclusive = true, bool preventDefault = false) { if (exclusive) { _jsRuntime.InvokeAsync(JSInteropConstants.AddDomEventListener, dom, eventName, preventDefault, DotNetObjectReference.Create(new Invoker((p) => { callback(p); }))); } else { string key = FormatKey(dom, eventName); if (!_domEventListeners.ContainsKey(key)) { _domEventListeners[key] = new List(); _jsRuntime.InvokeAsync(JSInteropConstants.AddDomEventListener, dom, eventName, preventDefault, DotNetObjectReference.Create(new Invoker((p) => { for (var i = 0; i < _domEventListeners[key].Count; i++) { var subscription = _domEventListeners[key][i]; object tP = JsonSerializer.Deserialize(p, subscription.Type); subscription.Delegate.DynamicInvoke(tP); } }))); } _domEventListeners[key].Add(new DomEventSubscription(callback, typeof(T))); } } public void AddEventListenerToFirstChild(object dom, string eventName, Action callback, bool preventDefault = false) { AddEventListenerToFirstChildInternal(dom, eventName, preventDefault, (e) => { JsonElement jsonElement = JsonDocument.Parse(e).RootElement; callback(jsonElement); }); } public void AddEventListenerToFirstChild(object dom, string eventName, Action callback, bool preventDefault = false) { AddEventListenerToFirstChildInternal(dom, eventName, preventDefault, (e) => { T obj = JsonSerializer.Deserialize(e); callback(obj); }); } public async ValueTask AddResizeObserver(ElementReference dom, Action> callback) { string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize)); if (!(await IsResizeObserverSupported())) { Action action = (je) => callback.Invoke(new List { new ResizeObserverEntry() }); AddEventListener("window", "resize", action, false); } else { if (!_domEventListeners.ContainsKey(key)) { _domEventListeners[key] = new List(); await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Create, key, DotNetObjectReference.Create(new Invoker((p) => { for (var i = 0; i < _domEventListeners[key].Count; i++) { var subscription = _domEventListeners[key][i]; object tP = JsonSerializer.Deserialize(p, subscription.Type); subscription.Delegate.DynamicInvoke(tP); } }))); await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Observe, key, dom); } _domEventListeners[key].Add(new DomEventSubscription(callback, typeof(List))); } } public async ValueTask RemoveResizeObserver(ElementReference dom, Action> callback) { string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize)); if (_domEventListeners.ContainsKey(key)) { var subscription = _domEventListeners[key].SingleOrDefault(s => s.Delegate == (Delegate)callback); if (subscription != null) { _domEventListeners[key].Remove(subscription); } } } public async ValueTask DisposeResizeObserver(ElementReference dom) { string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize)); if (await IsResizeObserverSupported()) { await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Dispose, key); } _domEventListeners.TryRemove(key, out _); } public async ValueTask DisconnectResizeObserver(ElementReference dom) { string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize)); if (await IsResizeObserverSupported()) { await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Disconnect, key); } if (_domEventListeners.ContainsKey(key)) { _domEventListeners[key].Clear(); } } private static string FormatKey(object dom, string eventName) => $"{dom}-{eventName}"; public void RemoveEventListerner(object dom, string eventName, Action callback) { string key = FormatKey(dom, eventName); if (_domEventListeners.ContainsKey(key)) { var subscription = _domEventListeners[key].SingleOrDefault(s => s.Delegate == (Delegate)callback); if (subscription != null) { _domEventListeners[key].Remove(subscription); } } } private async ValueTask IsResizeObserverSupported() => _isResizeObserverSupported ??= await _jsRuntime.IsResizeObserverSupported(); } public class Invoker { private Action _action; public Invoker(Action invoker) { this._action = invoker; } [JSInvokable] public void Invoke(T param) { _action.Invoke(param); } } }