mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-04-04 06:52:16 -04:00
Autofocus password field on web app unlock screen (#1269)
This commit is contained in:
committed by
Leendert de Borst
parent
938e8869f2
commit
9a367acbdc
@@ -1,13 +1,16 @@
|
||||
@using System.Linq.Expressions
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
||||
<div class="relative">
|
||||
<InputText @attributes="AdditionalAttributes"
|
||||
@ref="inputComponent"
|
||||
id="@Id"
|
||||
Value="@Value"
|
||||
ValueChanged="ValueChanged"
|
||||
ValueExpression="ValueExpression"
|
||||
type="@(_showPassword ? "text" : "password")"
|
||||
placeholder="@Placeholder"
|
||||
autocomplete="off"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 pr-10 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" />
|
||||
|
||||
<button type="button"
|
||||
@@ -32,6 +35,7 @@
|
||||
|
||||
@code {
|
||||
private bool _showPassword = false;
|
||||
private InputText? inputComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the input field.
|
||||
@@ -76,4 +80,29 @@
|
||||
{
|
||||
_showPassword = !_showPassword;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Focuses the input field.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous focus operation.</returns>
|
||||
public async Task FocusAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (inputComponent?.Element != null)
|
||||
{
|
||||
await inputComponent.Element.Value.FocusAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to JS focus if component element is not available
|
||||
await JSRuntime.InvokeVoidAsync("eval", $"document.getElementById('{Id}')?.focus()");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Final fallback to JS focus if ElementReference focus fails
|
||||
await JSRuntime.InvokeVoidAsync("eval", $"document.getElementById('{Id}')?.focus()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
@page "/unlock/{SkipWebAuthn:bool}"
|
||||
@inherits AliasVault.Client.Auth.Pages.Base.LoginBase
|
||||
@inject ILogger<Unlock> Logger
|
||||
@inject IJSRuntime JSRuntime
|
||||
@layout Auth.Layout.MainLayout
|
||||
@using System.Text.Json
|
||||
@using AliasVault.Client.Auth.Components
|
||||
@@ -49,7 +50,7 @@ else
|
||||
<svg class="w-5 h-5 mr-2 -ml-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"></path><path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd"></path></svg>
|
||||
@Localizer["UnlockWithWebAuthn"]
|
||||
</button>
|
||||
<button type="button" @onclick="ShowPasswordLogin" class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-center text-gray-900 rounded-lg border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 dark:text-white dark:border-gray-700 dark:hover:bg-gray-700 dark:focus:ring-gray-800">
|
||||
<button type="button" @onclick="async () => await ShowPasswordLogin()" class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-center text-gray-900 rounded-lg border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 dark:text-white dark:border-gray-700 dark:hover:bg-gray-700 dark:focus:ring-gray-800">
|
||||
@Localizer["UnlockWithPassword"]
|
||||
</button>
|
||||
</div>
|
||||
@@ -67,7 +68,7 @@ else
|
||||
<DataAnnotationsValidator/>
|
||||
<div>
|
||||
<label asp-for="Input.Password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["YourPasswordLabel"]</label>
|
||||
<PasswordInputField id="password" @bind-Value="_unlockModel.Password" placeholder="••••••••"/>
|
||||
<PasswordInputField @ref="passwordField" id="password" @bind-Value="_unlockModel.Password" placeholder="••••••••"/>
|
||||
<ValidationMessage For="() => _unlockModel.Password"/>
|
||||
</div>
|
||||
|
||||
@@ -96,9 +97,11 @@ else
|
||||
private bool IsLoading { get; set; } = true;
|
||||
private bool IsWebAuthnLoading { get; set; }
|
||||
private bool ShowWebAuthnButton { get; set; }
|
||||
private bool IsPasswordFocused { get; set; }
|
||||
private readonly UnlockModel _unlockModel = new();
|
||||
private FullScreenLoadingIndicator _loadingIndicator = new();
|
||||
private ServerValidationErrors _serverValidationErrors = new();
|
||||
private PasswordInputField? passwordField;
|
||||
private IStringLocalizer Localizer => LocalizerFactory.Create("Pages.Auth.Unlock", "AliasVault.Client");
|
||||
private IStringLocalizer ApiErrorLocalizer => LocalizerFactory.Create("ApiErrors", "AliasVault.Client");
|
||||
|
||||
@@ -123,6 +126,12 @@ else
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
if (!IsLoading && !IsWebAuthnLoading && !ShowWebAuthnButton && !IsPasswordFocused)
|
||||
{
|
||||
IsPasswordFocused = true;
|
||||
await FocusPasswordField();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -314,9 +323,34 @@ else
|
||||
/// <summary>
|
||||
/// Show the password login form.
|
||||
/// </summary>
|
||||
private void ShowPasswordLogin()
|
||||
private async Task ShowPasswordLogin()
|
||||
{
|
||||
ShowWebAuthnButton = false;
|
||||
StateHasChanged();
|
||||
|
||||
// Focus the password field after the form is rendered
|
||||
if (!IsPasswordFocused)
|
||||
{
|
||||
IsPasswordFocused = true;
|
||||
await FocusPasswordField();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Focus the password input field using component reference.
|
||||
/// </summary>
|
||||
private async Task FocusPasswordField()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (passwordField != null)
|
||||
{
|
||||
await passwordField.FocusAsync();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3472,10 +3472,6 @@ video {
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.lg\:relative {
|
||||
position: relative;
|
||||
}
|
||||
@@ -3536,10 +3532,6 @@ video {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lg\:min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.lg\:w-1\/2 {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user