Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 fix(OtpInput): error occurs when setting value form a non-empty to an empty value #2074

Merged
merged 2 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Masa.Blazor/Components/OtpInput/MOtpInput.razor
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
id="@Id"
@ref="Ref"
@attributes="@Attributes">
@for (var i = 0; i < @Length; i++)
@for (var i = 0; i < Length; i++)
{
var index = i;
@GenItem(index)
Expand All @@ -18,7 +18,7 @@

private RenderFragment GenItem(int index) => __builder =>
{
var value = Values[index];
var value = _otp[index];
<div class="@GetContentClass()">
<div class="@_inputBlock.Element("control")">
<div class="@GetClass(_inputBlock.Element("slot").Name, CssClassUtils.GetBackgroundColor(BackgroundColor))"
Expand All @@ -30,7 +30,7 @@
</legend>
</fieldset>
<div class="@_textFieldBlock.Element("slot")">
<input @ref="InputRefs[index]"
<input @ref="_inputRefs[index]"
type="@Type.ToString()"
readonly="@Readonly"
min="@(Type == OtpInputType.Number ? 0 : null)"
Expand Down
90 changes: 49 additions & 41 deletions src/Masa.Blazor/Components/OtpInput/MOtpInput.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,17 @@ public bool IsDark
private int _prevFocusIndex;
private string? _prevValue;

private List<ElementReference> InputRefs { get; } = new();
private List<ElementReference> _inputRefs = [];

private List<string> Values { get; set; } = new();
/// <summary>
/// Always keep the length of _otp equal to Length
/// </summary>
private List<string> _otp = [];

private string ComputedValue => string.Join("", Values);
/// <summary>
/// Get the latest value of otp
/// </summary>
private string ComputedValue => string.Join("", _otp);

protected override async Task OnParametersSetAsync()
{
Expand All @@ -88,48 +94,50 @@ protected override async Task OnParametersSetAsync()
#endif
if (_prevValue != Value)
{
Value ??= string.Empty;
var prevValueLength = _prevValue?.Length ?? 0;
var valueLength = Value.Length;
_prevValue = Value;

if (Value != null)
for (var i = 0; i < Math.Max(prevValueLength, valueLength); i++)
{
for (int i = 0; i < Value.Length; i++)
if (i >= _otp.Count)
{
if (Values.Count < i + 1)
{
Values.Add(Value[i].ToString());
}
else
{
Values[i] = Value[i].ToString();
}
continue;
}

_otp[i] = i < valueLength ? Value[i].ToString() : string.Empty;
}

if (Value != ComputedValue)
{
_prevValue = ComputedValue;
await ValueChanged.InvokeAsync(_prevValue);
}
}

if (_prevLength != Length)
{
_prevLength = Length;

if (Values.Count > Length)
if (_otp.Count > Length)
{
for (int i = Length; i < Values.Count; i++)
{
Values.RemoveAt(i);
}
_otp = _otp.Take(Length).ToList();

if (ValueChanged.HasDelegate)
{
await ValueChanged.InvokeAsync(ComputedValue);
_prevValue = ComputedValue;
await ValueChanged.InvokeAsync(_prevValue);
}
}
else
{
for (int i = 0; i < Length; i++)
for (var i = 0; i < Length; i++)
{
if (Values.Count() < (i + 1))
Values.Add(string.Empty);
if (InputRefs.Count() < (i + 1))
InputRefs.Add(new ElementReference());
if (_otp.Count < (i + 1))
_otp.Add(string.Empty);
if (_inputRefs.Count < (i + 1))
_inputRefs.Add(new ElementReference());
}
}
}
Expand All @@ -142,13 +150,13 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
if (firstRender)
{
_handle = DotNetObjectReference.Create(new Invoker<OtpJsResult>(GetResultFromJs));
await Js.InvokeVoidAsync(JsInteropConstants.RegisterOTPInputOnInputEvent, InputRefs, _handle);
await Js.InvokeVoidAsync(JsInteropConstants.RegisterOTPInputOnInputEvent, _inputRefs, _handle);

NextTick(() =>
{
if (AutoFocus && InputRefs.Count > 0)
if (AutoFocus && _inputRefs.Count > 0)
{
_ = InputRefs[0].FocusAsync();
_ = _inputRefs[0].FocusAsync();
}
});
}
Expand Down Expand Up @@ -200,7 +208,7 @@ private async Task GetResultFromJs(OtpJsResult result)
await OnInput.InvokeAsync(result.Value);
}

if (result.Index >= Length - 1 && !Values.Any(p => string.IsNullOrEmpty(p)) && OnFinish.HasDelegate)
if (result.Index >= Length - 1 && !_otp.Any(p => string.IsNullOrEmpty(p)) && OnFinish.HasDelegate)
{
await OnFinish.InvokeAsync(ComputedValue);
}
Expand Down Expand Up @@ -232,7 +240,7 @@ private async Task FocusAsync(int index)

if (containsActiveElement)
{
var item = InputRefs[index];
var item = _inputRefs[index];
await item.FocusAsync();
}
}
Expand All @@ -241,37 +249,37 @@ private async Task FocusAsync(int index)

private int GetFirstEmptyIndex()
{
for (int i = 0; i < Values.Count; i++)
for (int i = 0; i < _otp.Count; i++)
{
if (string.IsNullOrEmpty(Values[i]))
if (string.IsNullOrEmpty(_otp[i]))
{
return i;
}
}

return Values.Count - 1;
return _otp.Count - 1;
}

private async Task ApplyValues(int index, string value)
{
var temp = new List<string>(Values.ToArray());
var temp = new List<string>(_otp.ToArray());
temp[index] = value;
temp.RemoveAll(p => string.IsNullOrEmpty(p));

Values[index] = value;
_otp[index] = value;

await Task.Yield();
await InvokeAsync(StateHasChanged);

Values[index] = String.Empty;
_otp[index] = String.Empty;

Values = temp;
_otp = temp;

var count = temp.Count;

for (int i = 0; i < this.Length - count; i++)
{
Values.Add(String.Empty);
_otp.Add(String.Empty);
}

await InvokeAsync(StateHasChanged);
Expand All @@ -295,13 +303,13 @@ private async Task HandleOnPasteAsync(OtpInputEventArgs<PasteWithDataEventArgs>
if (changeIndex >= this.Length)
break;

Values[changeIndex] = clipboardData[i].ToString();
_otp[changeIndex] = clipboardData[i].ToString();
}

var newFocusIndex = Math.Min(events.Index + clipboardData.Length - 1, this.Length - 1);
await FocusAsync(newFocusIndex);

var hasEmptyValue = Values.Any(string.IsNullOrWhiteSpace);
var hasEmptyValue = _otp.Any(string.IsNullOrWhiteSpace);

if (!hasEmptyValue)
{
Expand All @@ -324,7 +332,7 @@ private async Task OnPasteWithDataAsync(PasteWithDataEventArgs args)

private async Task HandleOnInputSlotClick(int optIndex)
{
if (IsFocused || IsDisabled || InputRefs.ElementAtOrDefault(optIndex).Context is null)
if (IsFocused || IsDisabled || _inputRefs.ElementAtOrDefault(optIndex).Context is null)
{
return;
}
Expand All @@ -335,6 +343,6 @@ private async Task HandleOnInputSlotClick(int optIndex)
protected override async ValueTask DisposeAsyncCore()
{
_handle?.Dispose();
await Js.InvokeVoidAsync(JsInteropConstants.UnregisterOTPInputOnInputEvent, InputRefs);
await Js.InvokeVoidAsync(JsInteropConstants.UnregisterOTPInputOnInputEvent, _inputRefs);
}
}
Loading