FRONT-I-01: Extract Auth components to Razor Class Library packages/blazor-ui/
- Created GoodGo.BlazorUi RCL (net10.0, MudBlazor 8.15) at packages/blazor-ui/
- Moved AuthButton, AuthCard, AuthInput, OtpInput, BrandPanel, SocialLogin, LanguageSwitcher
- Referenced RCL from WebClientTpos.Client via ProjectReference
- Added GoodGo.BlazorUi.Components.Auth/Common namespaces to _Imports.razor
FRONT-I-02: Add ARIA/accessibility attributes (WCAG 2.1 AA)
- AuthButton: aria-label, aria-busy, aria-disabled, aria-hidden on decorative icons
- OtpInput: role=group, aria-label per digit, autocomplete=one-time-code
- PosLayout: aria-expanded + aria-controls on sidebar/order toggles, aria-label on all icon buttons
FRONT-I-03: Implement Style Dictionary design token pipeline
- Created packages/design-tokens/ with token JSON (color, spacing, typography, border)
- Style Dictionary config outputs: CSS custom properties → wwwroot/css/tokens.generated.css
- Second output: C# constants → packages/blazor-ui/DesignTokens/DesignTokens.g.cs
- Added tokens:build script to root package.json
- Added tokens.generated.css link to index.html (before app.css for cascade correctness)
FRONT-I-04: Replace eval() in OtpInput with safe JS interop
- Created wwwroot/js/otp-input.js with window.focusOtpInput(index) helper
- Replaced JS.InvokeVoidAsync("eval", ...) with JS.InvokeVoidAsync("focusOtpInput", index)
- Eliminates CSP-violating eval(), improves security and debuggability
Co-Authored-By: Paperclip <noreply@paperclip.ing>
76 lines
2.6 KiB
Plaintext
76 lines
2.6 KiB
Plaintext
@*
|
|
EN: Dark-themed auth input with label, prefix icon, and action button (e.g., password toggle).
|
|
VI: Input xác thực dark theme với label, icon prefix, và action button (VD: toggle mật khẩu).
|
|
*@
|
|
|
|
<div class="auth-field">
|
|
@if (Label != null || ForgotPasswordLink != null)
|
|
{
|
|
<div class="auth-label-row">
|
|
@if (Label != null)
|
|
{
|
|
<label class="auth-label" for="@InputId">@Label</label>
|
|
}
|
|
@if (ForgotPasswordLink != null)
|
|
{
|
|
<a href="@ForgotPasswordLink" class="auth-label-link">@ForgotPasswordText</a>
|
|
}
|
|
</div>
|
|
}
|
|
<div class="auth-input-wrapper">
|
|
@if (PrefixIcon != null)
|
|
{
|
|
<span class="auth-input-icon">
|
|
<i data-lucide="@PrefixIcon"></i>
|
|
</span>
|
|
}
|
|
<input id="@InputId"
|
|
type="@_currentType"
|
|
class="auth-input @(PrefixIcon == null ? "auth-input--no-icon" : "")"
|
|
placeholder="@Placeholder"
|
|
value="@Value"
|
|
autocomplete="@AutoComplete"
|
|
@oninput="OnInput"
|
|
@onchange="OnChange" />
|
|
@if (InputType == "password")
|
|
{
|
|
<button type="button" class="auth-input-action" @onclick="TogglePassword" title="Toggle password visibility">
|
|
<i data-lucide="@(_showPassword ? "eye-off" : "eye")"></i>
|
|
</button>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
@code {
|
|
private bool _showPassword = false;
|
|
private string _currentType => InputType == "password" && _showPassword ? "text" : InputType;
|
|
|
|
[Parameter] public string? Label { get; set; }
|
|
[Parameter] public string InputType { get; set; } = "text";
|
|
[Parameter] public string? Placeholder { get; set; }
|
|
[Parameter] public string? Value { get; set; }
|
|
[Parameter] public string? PrefixIcon { get; set; }
|
|
[Parameter] public string? InputId { get; set; }
|
|
[Parameter] public string? AutoComplete { get; set; }
|
|
[Parameter] public string? ForgotPasswordLink { get; set; }
|
|
[Parameter] public string ForgotPasswordText { get; set; } = "Quên mật khẩu?";
|
|
[Parameter] public EventCallback<ChangeEventArgs> ValueChanged { get; set; }
|
|
|
|
private async Task OnInput(ChangeEventArgs e)
|
|
{
|
|
Value = e.Value?.ToString();
|
|
await ValueChanged.InvokeAsync(e);
|
|
}
|
|
|
|
private async Task OnChange(ChangeEventArgs e)
|
|
{
|
|
Value = e.Value?.ToString();
|
|
await ValueChanged.InvokeAsync(e);
|
|
}
|
|
|
|
private void TogglePassword()
|
|
{
|
|
_showPassword = !_showPassword;
|
|
}
|
|
}
|