mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
The target gear painted a heavier 44 px box on mobile against the Coach icon's flat 40 px, so the header cluster read as two mismatched icons. Settle the gear on a flat 40 px box that equals the Coach sparkle on every breakpoint, and restore the 44 px WCAG 2.5.5 touch target through a transparent ::before overlay rather than a larger painted box. The shared icon-button size stays untouched, so other icon affordances are unaffected.
69 lines
2.6 KiB
TypeScript
69 lines
2.6 KiB
TypeScript
"use client";
|
||
|
||
import { Settings2 } from "lucide-react";
|
||
|
||
import { Button } from "@/components/ui/button";
|
||
import { cn } from "@/lib/utils";
|
||
import { useTranslations } from "@/lib/i18n/context";
|
||
import { useTargetAdjust } from "@/lib/insights/target-adjust-context";
|
||
|
||
/**
|
||
* Header gear that opens the per-metric target editor.
|
||
*
|
||
* The "Adjust target range" affordance used to sit inside the target
|
||
* reference card; it now lives in the Insights category-page header
|
||
* (`<SubPageShell>`), to the left of the Coach launch icon, so the card
|
||
* stays a read surface. The button is a thin wrapper around
|
||
* `useTargetAdjust()`:
|
||
*
|
||
* - renders nothing when no provider is mounted or no editable target
|
||
* has registered for the page (e.g. a metric without a numeric
|
||
* target band, where the card paints nothing), so the header never
|
||
* shows a dead control;
|
||
* - opens the primary registered target's `<TargetEditSheet>` on click.
|
||
*
|
||
* Sized to match `<CoachLaunchButton variant="icon">` so the gear + the
|
||
* Coach sparkle read as a consistent icon cluster top-right of the
|
||
* heading.
|
||
*/
|
||
export function TargetAdjustButton({ className }: { className?: string }) {
|
||
const { t } = useTranslations();
|
||
const adjust = useTargetAdjust();
|
||
|
||
// No provider, or no editable target registered for this page.
|
||
if (!adjust || !adjust.canAdjust) return null;
|
||
|
||
// Reuse the action's existing copy ("Adjust target range" /
|
||
// "Zielbereich anpassen") — the gear performs exactly what the old
|
||
// in-card link did, so the same string is the right accessible label.
|
||
const label = t("insights.subPage.target.adjustLink");
|
||
|
||
return (
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
data-slot="target-adjust-trigger"
|
||
aria-label={label}
|
||
title={label}
|
||
onClick={() => adjust.requestAdjust()}
|
||
className={cn(
|
||
// The `icon` size is a flat 40 px — identical to the sibling Coach
|
||
// sparkle icon — so the gear + Coach read as one even-weighted icon
|
||
// cluster top-right of the heading on every breakpoint.
|
||
//
|
||
// The 40 px visual box is below the WCAG 2.5.5 (44 px) floor, so a
|
||
// transparent `::before` overlay extends the *hit* area by 6 px on
|
||
// every side (40 + 2×6 = 52 px) without enlarging the painted box.
|
||
// This keeps the touch target comfortable while the gear stays
|
||
// visually equal to the Coach icon.
|
||
"text-muted-foreground hover:text-foreground relative size-10",
|
||
"before:absolute before:-inset-1.5 before:content-['']",
|
||
className,
|
||
)}
|
||
>
|
||
<Settings2 className="size-4" aria-hidden="true" />
|
||
</Button>
|
||
);
|
||
}
|