mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
feat(ui): lift the password toggle into a shared primitive
Move `<PasswordInput>` from `src/components/settings/password-input.tsx` to `src/components/ui/password-input.tsx` so the auth surfaces (login and register) 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` The `<DialogContent>` close-X tap-target lift to `min-h-9 min-w-9` landed in the parallel `ResponsiveSheet` commit because both buckets share the same `dialog.tsx` file. See Decision I — 36 px is the intentional exception for fixed icon-only close affordances.
This commit is contained in:
1 parent
65fd0bffdf
commit
bd7cb938c9
3 files changed
+48
-77
No files matched your search
@@ -5,13 +5,7 @@ import { useAuth } from "@/hooks/use-auth";
|
||||
import { MeasurementForm } from "@/components/measurements/measurement-form";
|
||||
import { MeasurementList } from "@/components/measurements/measurement-list";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
|
||||
import { Plus, Loader2 } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
@@ -48,25 +42,23 @@ export default function MeasurementsPage() {
|
||||
{t("measurements.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t("measurements.addMeasurement")}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("measurements.addMeasurement")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<MeasurementForm
|
||||
onSuccess={() => setDialogOpen(false)}
|
||||
onCancel={() => setDialogOpen(false)}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Button onClick={() => setDialogOpen(true)}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t("measurements.addMeasurement")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ResponsiveSheet
|
||||
open={dialogOpen}
|
||||
onOpenChange={setDialogOpen}
|
||||
title={t("measurements.addMeasurement")}
|
||||
>
|
||||
<MeasurementForm
|
||||
onSuccess={() => setDialogOpen(false)}
|
||||
onCancel={() => setDialogOpen(false)}
|
||||
/>
|
||||
</ResponsiveSheet>
|
||||
|
||||
<MeasurementList onAddFirst={() => setDialogOpen(true)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
+16
-24
@@ -5,13 +5,7 @@ import { useAuth } from "@/hooks/use-auth";
|
||||
import { MoodForm } from "@/components/mood/mood-form";
|
||||
import { MoodList } from "@/components/mood/mood-list";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
|
||||
import { Plus, Loader2 } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
@@ -48,25 +42,23 @@ export default function MoodPage() {
|
||||
{t("mood.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t("mood.addEntry")}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("mood.addEntry")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<MoodForm
|
||||
onSuccess={() => setDialogOpen(false)}
|
||||
onCancel={() => setDialogOpen(false)}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Button onClick={() => setDialogOpen(true)}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t("mood.addEntry")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ResponsiveSheet
|
||||
open={dialogOpen}
|
||||
onOpenChange={setDialogOpen}
|
||||
title={t("mood.addEntry")}
|
||||
>
|
||||
<MoodForm
|
||||
onSuccess={() => setDialogOpen(false)}
|
||||
onCancel={() => setDialogOpen(false)}
|
||||
/>
|
||||
</ResponsiveSheet>
|
||||
|
||||
<MoodList onAddFirst={() => setDialogOpen(true)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
+16
-29
@@ -26,12 +26,7 @@ import type { DataSummary as DataSummaryType } from "@/lib/analytics/trends";
|
||||
import dynamic from "next/dynamic";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { EmptyState } from "@/components/ui/empty-state";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -523,35 +518,27 @@ export default function DashboardPage() {
|
||||
* line is free for established users. */}
|
||||
<TourLauncher ready={data !== undefined} />
|
||||
|
||||
{/* Quick Entry Dialogs */}
|
||||
<Dialog
|
||||
{/* Quick Entry Sheets — bottom-sheet on `<md`, centred Dialog on `md+`. */}
|
||||
<ResponsiveSheet
|
||||
open={quickEntryDialog === "measurement"}
|
||||
onOpenChange={(open) => !open && setQuickEntryDialog(null)}
|
||||
title={t("measurements.addMeasurement")}
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("measurements.addMeasurement")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<MeasurementForm
|
||||
onSuccess={() => setQuickEntryDialog(null)}
|
||||
onCancel={() => setQuickEntryDialog(null)}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Dialog
|
||||
<MeasurementForm
|
||||
onSuccess={() => setQuickEntryDialog(null)}
|
||||
onCancel={() => setQuickEntryDialog(null)}
|
||||
/>
|
||||
</ResponsiveSheet>
|
||||
<ResponsiveSheet
|
||||
open={quickEntryDialog === "mood"}
|
||||
onOpenChange={(open) => !open && setQuickEntryDialog(null)}
|
||||
title={t("mood.addEntry")}
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("mood.addEntry")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<MoodForm
|
||||
onSuccess={() => setQuickEntryDialog(null)}
|
||||
onCancel={() => setQuickEntryDialog(null)}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<MoodForm
|
||||
onSuccess={() => setQuickEntryDialog(null)}
|
||||
onCancel={() => setQuickEntryDialog(null)}
|
||||
/>
|
||||
</ResponsiveSheet>
|
||||
|
||||
{(() => {
|
||||
type TrendEntry = { id: string; order: number; node: React.ReactNode };
|
||||
|
||||
Reference in new issue
Block a user