Files
pos-system/microservices/packages/blazor-ui/Components/Auth/OtpInput.razor
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

100 lines
3.2 KiB
Plaintext

@*
EN: 6-digit OTP input with auto-focus, auto-advance, and backspace support.
VI: Input OTP 6 chữ số với auto-focus, tự chuyển ô, và hỗ trợ backspace.
*@
@inject IJSRuntime JS
<div class="auth-otp-group"
role="group"
aria-label="@GroupAriaLabel">
@for (int i = 0; i < DigitCount; i++)
{
var index = i;
<input id="otp-@index"
type="text"
inputmode="numeric"
maxlength="1"
autocomplete="one-time-code"
aria-label="@($"Digit {index + 1} of {DigitCount}")"
class="auth-otp-input @(UseBlueTheme ? "auth-otp-input--blue" : "") @(!string.IsNullOrEmpty(_digits[index]) ? "auth-otp-input--filled" : "")"
value="@_digits[index]"
@oninput="(e) => HandleInput(e, index)"
@onkeydown="(e) => HandleKeyDown(e, index)" />
}
</div>
@code {
private string[] _digits = new string[6];
/// <summary>
/// EN: Number of OTP digits.
/// VI: Số chữ số OTP.
/// </summary>
[Parameter] public int DigitCount { get; set; } = 6;
/// <summary>
/// EN: Use blue theme (for 2FA authenticator).
/// VI: Dùng theme xanh dương (cho 2FA authenticator).
/// </summary>
[Parameter] public bool UseBlueTheme { get; set; }
/// <summary>
/// EN: Accessible group label for screen readers.
/// VI: Nhãn nhóm accessible cho screen reader.
/// </summary>
[Parameter] public string GroupAriaLabel { get; set; } = "Enter OTP code";
/// <summary>
/// EN: Callback when all digits are entered.
/// VI: Callback khi tất cả chữ số được nhập.
/// </summary>
[Parameter] public EventCallback<string> OnComplete { get; set; }
protected override void OnInitialized()
{
_digits = new string[DigitCount];
}
private async Task HandleInput(ChangeEventArgs e, int index)
{
var value = e.Value?.ToString() ?? "";
// EN: Only allow numeric input
// VI: Chỉ cho phép nhập số
if (!string.IsNullOrEmpty(value) && !char.IsDigit(value[0]))
{
_digits[index] = "";
return;
}
_digits[index] = value;
if (!string.IsNullOrEmpty(value) && index < DigitCount - 1)
{
// EN: Auto-advance to next input
// VI: Tự động chuyển sang ô tiếp theo
await JS.InvokeVoidAsync("focusOtpInput", index + 1);
}
// EN: Check if all digits are filled
// VI: Kiểm tra tất cả ô đã nhập xong chưa
if (_digits.All(d => !string.IsNullOrEmpty(d)))
{
var code = string.Join("", _digits);
await OnComplete.InvokeAsync(code);
}
}
private async Task HandleKeyDown(KeyboardEventArgs e, int index)
{
if (e.Key == "Backspace" && string.IsNullOrEmpty(_digits[index]) && index > 0)
{
// EN: Move to previous input on backspace
// VI: Chuyển về ô trước khi nhấn backspace
_digits[index - 1] = "";
await JS.InvokeVoidAsync("focusOtpInput", index - 1);
}
}
}