Add password visibility toggle to login form
- Implement show/hide password functionality with eye icon toggle button - Add dynamic input type switching between password and text - Position toggle button inside password input field with proper styling - Include hover, focus states and accessibility features (tabindex, title) - Use FontAwesome eye/eye-slash icons for visual feedback - Maintain secure default (password hidden) with optional visibility - Integrate proper tab order with existing form elements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,9 @@ pub fn Login(props: &LoginProps) -> Html {
|
||||
let remember_server = use_state(|| true);
|
||||
let remember_username = use_state(|| true);
|
||||
|
||||
// Password visibility toggle
|
||||
let show_password = use_state(|| false);
|
||||
|
||||
let server_url_ref = use_node_ref();
|
||||
let username_ref = use_node_ref();
|
||||
let password_ref = use_node_ref();
|
||||
@@ -98,6 +101,13 @@ pub fn Login(props: &LoginProps) -> Html {
|
||||
})
|
||||
};
|
||||
|
||||
let on_toggle_password_visibility = {
|
||||
let show_password = show_password.clone();
|
||||
Callback::from(move |_| {
|
||||
show_password.set(!*show_password);
|
||||
})
|
||||
};
|
||||
|
||||
let on_submit = {
|
||||
let server_url = server_url.clone();
|
||||
let username = username.clone();
|
||||
@@ -242,16 +252,27 @@ pub fn Login(props: &LoginProps) -> Html {
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">{"Password"}</label>
|
||||
<input
|
||||
ref={password_ref}
|
||||
type="password"
|
||||
id="password"
|
||||
placeholder="Enter your password"
|
||||
value={(*password).clone()}
|
||||
onchange={on_password_change}
|
||||
disabled={*is_loading}
|
||||
tabindex="3"
|
||||
/>
|
||||
<div class="password-input-container">
|
||||
<input
|
||||
ref={password_ref}
|
||||
type={if *show_password { "text" } else { "password" }}
|
||||
id="password"
|
||||
placeholder="Enter your password"
|
||||
value={(*password).clone()}
|
||||
onchange={on_password_change}
|
||||
disabled={*is_loading}
|
||||
tabindex="3"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="password-toggle-btn"
|
||||
onclick={on_toggle_password_visibility}
|
||||
tabindex="6"
|
||||
title={if *show_password { "Hide password" } else { "Show password" }}
|
||||
>
|
||||
<i class={if *show_password { "fas fa-eye-slash" } else { "fas fa-eye" }}></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
|
||||
@@ -534,6 +534,41 @@ body {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.password-input-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.password-input-container input {
|
||||
padding-right: 3rem !important;
|
||||
}
|
||||
|
||||
.password-toggle-btn {
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: color 0.2s ease;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.password-toggle-btn:hover {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.password-toggle-btn:focus {
|
||||
outline: none;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.login-button, .register-button {
|
||||
width: 100%;
|
||||
padding: var(--control-padding);
|
||||
|
||||
Reference in New Issue
Block a user