mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(settings): tile-padding parity, passkey breakpoint, scroll-into-view, F17 threshold toggle
Five small polish items from the v1.4.33 audit, plus the
maintainer's scroll-into-view item:
* Export-section card padding promoted from `p-5` to `p-6` so the
Export grid rhythm matches every other Settings card.
* Passkey list flips from a desktop table to a mobile card list at
`lg` instead of `md`. iPad portrait (768 px) used to sit on the
`md` inflection and flip-flop between the two layouts on
rotation; the card layout reads fine on iPad portrait, the table
needs ~620 px of column width to show the destructive delete
action without horizontal scroll.
* `<SettingsShell>` mobile chip strip now scrolls its active chip
into view on route change (`block: "nearest", inline: "center",
behavior: "smooth"`). On a 393 CSS px viewport the 11-chip strip
pushes the rightmost four chips below the fold; without the
scroll-into-view, tapping one of those chips left the strip
pinned to the leftmost chip on the new page, which read as a
broken nav. Smooth scroll honours the user's
`prefers-reduced-motion` via the browser's own auto-detection.
* F17 — `thresholds-editor-section` toggle label anchors on the
*action* it performs ("Eigene Werte" / "Custom range") instead
of mirroring the current state ("Auto" / "Überschrieben"). The
old label read as "Auto is off, why are there no inputs?" when
the toggle was off and inputs were hidden; the new label
unambiguously announces "flip me to enter a custom range". The
`thresholds.sourceOverride` badge still surfaces when the
override is the active source. Adds
`thresholds.overrideToggleLabel`.
Locale parity across de, en, es, fr, it, pl.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a8a5887768
commit
73043d42d1
10 files changed
+57
-10
No files matched your search
@@ -1321,6 +1321,7 @@
|
||||
"defaultLabel": "Default",
|
||||
"autoModeLabel": "Auto",
|
||||
"overrideModeLabel": "Überschrieben",
|
||||
"overrideToggleLabel": "Eigene Werte",
|
||||
"resetAction": "Auf Default zurücksetzen",
|
||||
"resetAllAction": "Alle auf Default zurücksetzen",
|
||||
"minLabel": "Min",
|
||||
|
||||
@@ -1321,6 +1321,7 @@
|
||||
"defaultLabel": "Default",
|
||||
"autoModeLabel": "Auto",
|
||||
"overrideModeLabel": "Override",
|
||||
"overrideToggleLabel": "Custom range",
|
||||
"resetAction": "Reset to default",
|
||||
"resetAllAction": "Reset all to default",
|
||||
"minLabel": "Min",
|
||||
|
||||
@@ -1321,6 +1321,7 @@
|
||||
"defaultLabel": "Default",
|
||||
"autoModeLabel": "Auto",
|
||||
"overrideModeLabel": "Override",
|
||||
"overrideToggleLabel": "Custom range",
|
||||
"resetAction": "Reset to default",
|
||||
"resetAllAction": "Reset all to default",
|
||||
"minLabel": "Mín",
|
||||
|
||||
@@ -1321,6 +1321,7 @@
|
||||
"defaultLabel": "Default",
|
||||
"autoModeLabel": "Auto",
|
||||
"overrideModeLabel": "Override",
|
||||
"overrideToggleLabel": "Custom range",
|
||||
"resetAction": "Reset to default",
|
||||
"resetAllAction": "Reset all to default",
|
||||
"minLabel": "Min",
|
||||
|
||||
@@ -1321,6 +1321,7 @@
|
||||
"defaultLabel": "Default",
|
||||
"autoModeLabel": "Auto",
|
||||
"overrideModeLabel": "Override",
|
||||
"overrideToggleLabel": "Custom range",
|
||||
"resetAction": "Reset to default",
|
||||
"resetAllAction": "Reset all to default",
|
||||
"minLabel": "Min",
|
||||
|
||||
@@ -1321,6 +1321,7 @@
|
||||
"defaultLabel": "Default",
|
||||
"autoModeLabel": "Auto",
|
||||
"overrideModeLabel": "Override",
|
||||
"overrideToggleLabel": "Custom range",
|
||||
"resetAction": "Reset to default",
|
||||
"resetAllAction": "Reset all to default",
|
||||
"minLabel": "Min",
|
||||
|
||||
@@ -740,7 +740,13 @@ function PasskeyListSection({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
table only at `≥ md`, and at `< md` paint a card-list where
|
||||
every passkey's name, device type, backup status, created
|
||||
date, and delete action are all visible without scrolling. */}
|
||||
<div className="border-border mt-3 hidden overflow-x-auto rounded-lg border md:block">
|
||||
{/* v1.4.33 — desktop table flips to card list at `lg` instead
|
||||
of `md` so iPad portrait (768 px = exactly the `md`
|
||||
inflection) lands on the card layout. The table needs
|
||||
~620 px of column width to read; below that it scrolls
|
||||
horizontally and the destructive delete column is the one
|
||||
that disappears. */}
|
||||
<div className="border-border mt-3 hidden overflow-x-auto rounded-lg border lg:block">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-muted/40 text-muted-foreground border-b text-xs">
|
||||
@@ -824,12 +830,15 @@ function PasskeyListSection({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Mobile card list — < md only. Each passkey gets its own
|
||||
card so the delete action stays visible and tap-targetable
|
||||
(44px) without the user having to horizontally scroll
|
||||
through a wide table. */}
|
||||
{/* Mobile + tablet card list — < lg only. Each passkey gets
|
||||
its own card so the delete action stays visible and
|
||||
tap-targetable (44 px) without the user having to
|
||||
horizontally scroll through a wide table. v1.4.33 bumped
|
||||
the breakpoint from `md` to `lg` so iPad portrait (768 px)
|
||||
stays on the card layout instead of flipping between
|
||||
layouts on rotation. */}
|
||||
<ul
|
||||
className="mt-3 space-y-2 md:hidden"
|
||||
className="mt-3 space-y-2 lg:hidden"
|
||||
data-testid="passkeys-mobile-list"
|
||||
>
|
||||
{passkeys.map((pk) => (
|
||||
|
||||
@@ -122,7 +122,7 @@ function ExportCardShell({
|
||||
return (
|
||||
<div
|
||||
data-testid={testId}
|
||||
className={`bg-card border-border flex h-full flex-col rounded-xl border p-5${outerClassName ? ` ${outerClassName}` : ""}`}
|
||||
className={`bg-card border-border flex h-full flex-col rounded-xl border p-6${outerClassName ? ` ${outerClassName}` : ""}`}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -135,6 +135,29 @@ export function SettingsShell({ active, children }: SettingsShellProps) {
|
||||
const { t } = useTranslations();
|
||||
const activeSlug = deriveActiveSlug(pathname, active);
|
||||
|
||||
// v1.4.33 IW4 — scroll the active chip into view inside the
|
||||
// horizontal mobile strip. On a 393 CSS px viewport the strip
|
||||
// is 11 chips wide and the rightmost four sit below the fold; a
|
||||
// user who tapped one of those chips lands on the new route
|
||||
// with the strip still scrolled to the leftmost chip, which
|
||||
// reads as if the navigation is broken. `inline: "center"`
|
||||
// anchors the active chip in the visible middle of its scroll
|
||||
// container so the user keeps spatial orientation between
|
||||
// sections.
|
||||
const mobileStripRef = React.useRef<HTMLElement | null>(null);
|
||||
React.useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
const strip = mobileStripRef.current;
|
||||
if (!strip) return;
|
||||
const active = strip.querySelector<HTMLElement>('[aria-current="page"]');
|
||||
if (!active) return;
|
||||
active.scrollIntoView({
|
||||
block: "nearest",
|
||||
inline: "center",
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, [activeSlug]);
|
||||
|
||||
// v1.4.25 W8 — AuthShell wraps the page in `px-4 py-6 md:px-6`
|
||||
// already, so this inner shell only carries the wider max-width.
|
||||
// Previously the duplicate `px-4 py-6 md:px-6 md:py-8` here was
|
||||
@@ -150,6 +173,7 @@ export function SettingsShell({ active, children }: SettingsShellProps) {
|
||||
page, which makes the page feel like every section card has
|
||||
an overflow problem. */}
|
||||
<nav
|
||||
ref={mobileStripRef}
|
||||
aria-label={t("settings.shell.sectionsNav")}
|
||||
className="no-scrollbar -mx-4 mb-4 overflow-x-auto px-4 md:hidden"
|
||||
>
|
||||
|
||||
@@ -250,10 +250,18 @@ function MetricRow({
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
{/* v1.4.33 F17 — the switch label used to flip between
|
||||
"Auto" and "Überschrieben" with the current state. The
|
||||
maintainer's audit caught that as confusing: a user
|
||||
looking at a row with "Auto" + the toggle off reads it
|
||||
as "Auto is off, why are there no inputs?" instead of
|
||||
"flip the switch to enter a custom range". Anchor the
|
||||
label on the *action* ("Eigene Werte" / "Custom range")
|
||||
so the affordance is unambiguous; the
|
||||
`thresholds.sourceOverride` badge to the right still
|
||||
announces when the override is active. */}
|
||||
<Label htmlFor={`override-${metric}`} className="text-xs">
|
||||
{overrideMode
|
||||
? t("thresholds.overrideModeLabel")
|
||||
: t("thresholds.autoModeLabel")}
|
||||
{t("thresholds.overrideToggleLabel")}
|
||||
</Label>
|
||||
<Switch
|
||||
id={`override-${metric}`}
|
||||
|
||||
Reference in new issue
Block a user