fix: multiple bugs originating from js (#1342)

* fix: multiple bugs originating from js

* fix(module:input): remove preventScroll from Focus method

fix: keep focus on clear

* fix(module:input): debug info clean-up

* fix(module:select): wait for browser to finish render

* fix(module:select): wait for ElementReference.Id

* fix doc assets

* fix(module:select): increase wait time for ElementRefernece.Id

Co-authored-by: James Yeung <shunjiey@hotmail.com>
This commit is contained in:
Andrzej Bakun 2021-04-15 08:19:26 +02:00 committed by GitHub
parent d4a7e29b77
commit 242084e774
13 changed files with 93 additions and 52 deletions

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using AntDesign.Core.Extensions;
namespace AntDesign
{
@ -103,6 +104,8 @@ namespace AntDesign
}
}
protected async Task FocusAsync(ElementReference target, bool preventScroll = false) => await Js.FocusAsync(target, preventScroll);
protected bool IsDisposed { get; private set; }
protected virtual void Dispose(bool disposing)

View File

@ -1,4 +1,7 @@
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace AntDesign.Core.Extensions
@ -6,5 +9,22 @@ namespace AntDesign.Core.Extensions
public static class JSRuntimeExtensions
{
public static bool IsBrowser(this IJSRuntime jsRuntime) => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
public static async Task FocusAsync(this IJSRuntime jSRuntime, ElementReference target, bool preventScroll = false)
{
#if NET5_0_OR_GREATER
await target.FocusAsync();
#else
try
{
await jSRuntime.InvokeVoidAsync(JSInteropConstants.Focus, target, preventScroll);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
#endif
}
}
}

View File

@ -164,8 +164,8 @@ export function addDomEventListener(element, eventName, preventDefault, invoker)
if (v instanceof Node) return 'Node';
if (v instanceof Window) return 'Window';
return v;
}, ' ');
invoker.invokeMethodAsync('Invoke', json);
}, ' ');
setTimeout(function () { invoker.invokeMethodAsync('Invoke', json) }, 0);
if (preventDefault === true) {
args.preventDefault();
}
@ -225,9 +225,9 @@ export function copy(text) {
export function focus(selector, noScroll: boolean=false) {
let dom = getDom(selector);
if (!(dom instanceof HTMLElement))
throw new Error("Unable to focus an invalid element.");
throw new Error("Unable to focus an invalid element.");
dom.focus({
preventScroll: noScroll
preventScroll: noScroll
})
}

View File

@ -163,7 +163,7 @@ namespace AntDesign
}
}
else
{
{
if (!_inputEnd.IsOnFocused)
{
await Blur(0);

View File

@ -439,7 +439,7 @@ namespace AntDesign
if (input != null)
{
input.IsOnFocused = true;
await JsInvokeAsync(JSInteropConstants.Focus, input.Ref);
await FocusAsync(input.Ref);
_needRefresh = true;
}
}

View File

@ -308,7 +308,7 @@ namespace AntDesign
private async Task SetFocus()
{
_focused = true;
await JsInvokeAsync(JSInteropConstants.Focus, Ref);
await FocusAsync(Ref);
}
private void OnInput(ChangeEventArgs args)

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@ -44,7 +45,15 @@ namespace AntDesign
public string Placeholder { get; set; }
[Parameter]
public bool AutoFocus { get; set; }
public bool AutoFocus
{
get { return _autoFocus; }
set {
_autoFocus = value;
if (!_isInitialized && _autoFocus)
IsFocused = _autoFocus;
}
}
[Parameter]
public TValue DefaultValue { get; set; }
@ -101,6 +110,9 @@ namespace AntDesign
private TValue _inputValue;
private bool _compositionInputting;
private Timer _debounceTimer;
private bool _autoFocus;
private bool _isInitialized;
private bool DebounceEnabled => DebounceMilliseconds != 0;
protected bool IsFocused { get; set; }
@ -115,6 +127,7 @@ namespace AntDesign
}
SetClasses();
_isInitialized = true;
}
protected virtual void SetClasses()
@ -173,11 +186,6 @@ namespace AntDesign
SetClasses();
}
public async Task Focus()
{
await JsInvokeAsync(JSInteropConstants.Focus, Ref);
}
protected virtual async Task OnChangeAsync(ChangeEventArgs args)
{
if (CurrentValueAsString != args?.Value?.ToString())
@ -238,6 +246,8 @@ namespace AntDesign
}
}
private async void OnFocusInternal(JsonElement e) => await OnFocusAsync(new());
internal virtual async Task OnFocusAsync(FocusEventArgs e)
{
IsFocused = true;
@ -274,11 +284,13 @@ namespace AntDesign
{
builder.AddAttribute(34, "Style", "visibility: visible;");
}
builder.AddAttribute(35, "OnClick", CallbackFactory.Create<MouseEventArgs>(this, (args) =>
builder.AddAttribute(35, "OnClick", CallbackFactory.Create<MouseEventArgs>(this, async (args) =>
{
CurrentValue = default;
IsFocused = true;
await this.FocusAsync(Ref);
if (OnChange.HasDelegate)
OnChange.InvokeAsync(Value);
await OnChange.InvokeAsync(Value);
ToggleClearBtn();
}));
builder.CloseComponent();
@ -334,11 +346,12 @@ namespace AntDesign
{
DomEventService.AddEventListener(Ref, "compositionstart", OnCompositionStart);
DomEventService.AddEventListener(Ref, "compositionend", OnCompositionEnd);
if (this.AutoFocus)
{
await this.Focus();
IsFocused = true;
await this.FocusAsync(Ref);
}
DomEventService.AddEventListener(Ref, "focus", OnFocusInternal, true);
}
}
@ -346,6 +359,7 @@ namespace AntDesign
{
DomEventService.RemoveEventListerner<JsonElement>(Ref, "compositionstart", OnCompositionStart);
DomEventService.RemoveEventListerner<JsonElement>(Ref, "compositionend", OnCompositionEnd);
DomEventService.RemoveEventListerner<JsonElement>(Ref, "focus", OnFocusInternal);
_debounceTimer?.Dispose();
@ -487,7 +501,9 @@ namespace AntDesign
builder.AddAttribute(73, "onkeydown", CallbackFactory.Create(this, OnkeyDownAsync));
builder.AddAttribute(74, "onkeyup", CallbackFactory.Create(this, OnKeyUpAsync));
builder.AddAttribute(75, "oninput", CallbackFactory.Create(this, OnInputAsync));
builder.AddAttribute(76, "onfocus", CallbackFactory.Create(this, OnFocusAsync));
//TODO: Use built in @onfocus once https://github.com/dotnet/aspnetcore/issues/30070 is solved
//builder.AddAttribute(76, "onfocus", CallbackFactory.Create(this, OnFocusAsync));
builder.AddAttribute(77, "onmouseup", CallbackFactory.Create(this, OnMouseUpAsync));
builder.AddElementReferenceCapture(90, r => Ref = r);
builder.CloseElement();

View File

@ -232,7 +232,7 @@ namespace AntDesign
if (OnFocus.HasDelegate)
await OnFocus.InvokeAsync(args);
await JsInvokeAsync(JSInteropConstants.Focus, Ref);
await FocusAsync(Ref);
StateHasChanged();
}

View File

@ -97,7 +97,7 @@ namespace AntDesign
{
if (this.AutoFocus)
{
await this.Focus();
await FocusAsync(this.InputRef);
}
await base.OnFirstAfterRenderAsync();
@ -140,11 +140,6 @@ namespace AntDesign
}
}
protected async Task Focus()
{
await JsInvokeAsync(JSInteropConstants.Focus, this.InputRef);
}
protected async Task Blur()
{
await JsInvokeAsync(JSInteropConstants.Blur, this.InputRef);

View File

@ -2079,7 +2079,7 @@ namespace AntDesign
SetClassMap();
await JsInvokeAsync(JSInteropConstants.Focus, _inputRef);
await FocusAsync(_inputRef);
OnFocus?.Invoke();
}

View File

@ -16,9 +16,7 @@
<input @ref="ParentSelect._inputRef"
@oninput="OnInput"
@onkeyup="OnKeyUp"
@onkeydown="OnKeyDown"
@onfocus="OnFocus"
@onblur="OnBlur"
@onkeydown="OnKeyDown"
@attributes=@AdditonalAttributes()
@onkeypress="@OnKeyPressEventHandler"
@onkeypress:preventDefault="@_suppressInput"
@ -252,9 +250,7 @@ else //ParentSelect.SelectMode != SelectMode.Default
<input @ref="ParentSelect._inputRef"
@oninput="OnInput"
@onkeyup="OnKeyUp"
@onkeydown="OnKeyDown"
@onfocus="OnFocus"
@onblur="OnBlur"
@onkeydown="OnKeyDown"
@attributes=@AdditonalAttributes()
@onkeypress="@OnKeyPressEventHandler"
@onkeypress:preventDefault="@_suppressInput"

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using AntDesign.Core.Extensions;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using AntDesign.Core.Extensions;
using AntDesign.JsInterop;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
@ -101,12 +101,16 @@ namespace AntDesign.Select.Internal
if (ParentSelect.IsResponsive)
{
_currentItemCount = ParentSelect.SelectedOptionItems.Count;
Console.WriteLine("Getting _aggregateTagElement from OnAfterRenderAsync");
//in blazor Wasm there has to be a delay here, otherwise _aggregateTagElement is not found and is set to null
if (Js.IsBrowser())
await Task.Delay(1);
//even though it is run in OnAfterRender, it may happen that the browser
//did not manage to render yet the element; force a continuous check
//until the element gets the id
while (_aggregateTag.Id is null)
{
await Task.Delay(5);
}
_aggregateTagElement = await Js.InvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, _aggregateTag, _aggregateTag.Id, true);
_aggregateTagElement = await Js.InvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, _aggregateTag);
if (_prefixRef.Id != default)
{
_prefixElement = await Js.InvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, _prefixRef);
@ -120,6 +124,8 @@ namespace AntDesign.Select.Internal
DomEventService.AddEventListener("window", "resize", OnWindowResize, false);
await CalculateResponsiveTags();
}
DomEventService.AddEventListener(ParentSelect._inputRef, "focusout", OnBlurInternal, true);
DomEventService.AddEventListener(ParentSelect._inputRef, "focus", OnFocusInternal, true);
}
else if (_currentItemCount != ParentSelect.SelectedOptionItems.Count)
{
@ -137,11 +143,13 @@ namespace AntDesign.Select.Internal
internal async Task CalculateResponsiveTags(bool forceInputFocus = false)
{
if (!ParentSelect.IsResponsive)
return;
_overflowElement = await Js.InvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, _overflow);
//distance between items is margin-inline-left=4px
decimal accumulatedWidth = _prefixElement.width + _suffixElement.width + (4 + (SearchValue?.Length ?? 0) * 8);
var accumulatedWidthSource = new StringBuilder(accumulatedWidth.ToString());
int i = 0;
bool overflowing = false;
bool renderAgain = false;
@ -179,7 +187,6 @@ namespace AntDesign.Select.Internal
else
{
accumulatedWidth += item.Width;
accumulatedWidthSource.Append("," + item.Width.ToString());
}
i++;
}
@ -198,16 +205,11 @@ namespace AntDesign.Select.Internal
var isFocused = await Js.InvokeAsync<bool>(JSInteropConstants.HasFocus, ParentSelect._inputRef);
if (!isFocused)
{
#if NET5_0
await ParentSelect._inputRef.FocusAsync();
#else
await Js.InvokeVoidAsync(JSInteropConstants.Focus, ParentSelect._inputRef);
#endif
await Js.FocusAsync(ParentSelect._inputRef);
}
}
}
private void SetInputWidth()
{
_inputWidth = string.Empty;
@ -373,6 +375,13 @@ namespace AntDesign.Select.Internal
await OnClearClick.InvokeAsync(args);
}
//TODO: Use built in @onfocus once https://github.com/dotnet/aspnetcore/issues/30070 is solved
private async void OnFocusInternal(JsonElement e) => await OnFocus.InvokeAsync(new());
//TODO: Use built in @onblur once https://github.com/dotnet/aspnetcore/issues/30070 is solved
private async void OnBlurInternal(JsonElement e) => await OnBlur.InvokeAsync(new());
public bool IsDisposed { get; private set; }
protected virtual void Dispose(bool disposing)
@ -386,6 +395,8 @@ namespace AntDesign.Select.Internal
await Js.InvokeVoidAsync(JSInteropConstants.RemovePreventEnterOnOverlayVisible, ParentSelect._inputRef);
});
}
DomEventService.RemoveEventListerner<JsonElement>(ParentSelect._inputRef, "focus", OnFocusInternal);
DomEventService.RemoveEventListerner<JsonElement>(ParentSelect._inputRef, "focusout", OnBlurInternal);
DomEventService.RemoveEventListerner<JsonElement>("window", "beforeunload", Reloading);
if (ParentSelect.IsResponsive)
DomEventService.RemoveEventListerner<JsonElement>("window", "resize", OnWindowResize);

View File

@ -660,8 +660,8 @@ namespace AntDesign
_right = false;
if (_mouseDown)
RightValue = _initialLeftValue;
LeftValue = rightV;
await JsInvokeAsync(JSInteropConstants.Focus, _leftHandle);
LeftValue = rightV;
await FocusAsync(_leftHandle);
}
else
{
@ -707,8 +707,8 @@ namespace AntDesign
_right = true;
if (_mouseDown)
LeftValue = _initialRightValue;
RightValue = leftV;
await JsInvokeAsync(JSInteropConstants.Focus, _rightHandle);
RightValue = leftV;
await FocusAsync(_rightHandle);
}
else
{