mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
refactor(ui): move PasswordInput into the shared UI layer with a 44 px toggle
Move `<PasswordInput>` from `src/components/settings/password-input.tsx` to `src/components/ui/password-input.tsx` so the auth surfaces and every settings card that masks a secret consume one primitive. The two surfaces previously diverged on toggle markup and aria-label wiring; the new home in `ui/` keeps them in lock-step. While the primitive moves, lift the icon-only show/hide toggle to a 44 px hit area per WCAG 2.5.5 — `inline-flex h-11 w-11` wrapper with the eye icon centred inside. The input itself gets `pr-12` so user input never collides with the toggle glyph. Consumer imports updated in: - `src/components/settings/account-section.tsx` - `src/components/settings/ai-section.tsx` - `src/components/settings/integrations-section.tsx` - `src/components/settings/ntfy-card.tsx` - `src/components/settings/telegram-card.tsx` - `src/components/admin/_shared.tsx`
This commit is contained in:
1 parent
9036e71516
commit
b25683404e
8 files changed
+58
-43
No files matched your search
@@ -0,0 +1,51 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
|
||||
/**
|
||||
* Password input with a built-in show/hide toggle. Lives in the shared UI
|
||||
* layer so every surface that needs a masked secret (Withings, moodLog,
|
||||
* ntfy, Telegram, AI provider keys, account password, auth login /
|
||||
* register) can share the same UX without duplicating the
|
||||
* visibility-toggle plumbing.
|
||||
*
|
||||
* v1.4.27 R3d MB2 — the icon-only toggle wraps in an `inline-flex h-11
|
||||
* w-11` hit area so the 44 px WCAG 2.5.5 floor is honoured on touch.
|
||||
* The icon stays at 16 px; the surrounding padding does the work. The
|
||||
* input itself gets `pr-12` so user input never collides with the
|
||||
* toggle glyph.
|
||||
*/
|
||||
export function PasswordInput({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof Input>) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { t } = useTranslations();
|
||||
return (
|
||||
<div className="relative">
|
||||
<Input
|
||||
{...props}
|
||||
type={visible ? "text" : "password"}
|
||||
className={cn("pr-12", className)}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
tabIndex={-1}
|
||||
// WCAG 2.1.2 button-name — the icon-only toggle has no visible text
|
||||
// and screen readers fall back to the icon's filename otherwise.
|
||||
aria-label={
|
||||
visible ? t("common.hidePassword") : t("common.showPassword")
|
||||
}
|
||||
onClick={() => setVisible((v) => !v)}
|
||||
className="text-muted-foreground hover:text-foreground absolute top-1/2 right-1 inline-flex h-11 w-11 -translate-y-1/2 items-center justify-center rounded-md"
|
||||
>
|
||||
{visible ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user