mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 05:42:19 +00:00
The export panel rendered twenty flat switches behind a collapsed disclosure, and the share-link form rendered none at all — it posted whatever the create schema defaulted to. Both now mount the same picker: twelve named groups, a count on each, the leaf grid one chevron away, and a fenced tier below a separator. The fence is mechanical, not a label. The sensitive tier has no group checkbox, so no single control anywhere in the picker can turn on more than one of mood, cycle, family history, PHQ-9, GAD-7, WHO-5 or SCI at a time. There is no select-all. The shipped template contains none of them. And the scope line under the button restates every sensitive inclusion by name rather than folding it into a count, because a number cannot be checked at a glance and "incl. Mood, PHQ-9" can. First run opens expanded with the named standard template applied and the fenced tier visibly empty, so the user presses Generate having seen twelve named groups. Every later run opens collapsed carrying their own last selection, which makes the waiting-room path two interactions. Only leaves are ever stored. A group's checkbox is derived from its leaves, so the two cannot disagree, and switching a group off and back on inside one session restores the pattern instead of flooding it. The cycle export card is deleted. It posted empty groups under a docblock claiming every other section was off, and empty groups resolved to the defaults — so a "cycle only" download carried weight, blood pressure, pulse, oxygen saturation, body fat, body composition, the medication list, adherence, labs, allergies and family history. Cycle is a fenced leaf in the one picker now, reachable for the PDF as well as for FHIR, which is more than the card offered. The panel gains a 30-day preset and a custom window, and its error line says what went wrong instead of interpolating a status code. The Checkbox primitive learns the indeterminate state it already had the Radix support for. A partly-selected group drawn as unchecked would say "none of this is included", which on this surface is a false statement about what leaves the instance.
66 lines
2.8 KiB
TypeScript
66 lines
2.8 KiB
TypeScript
"use client";
|
||
|
||
import * as React from "react";
|
||
import { Checkbox as CheckboxPrimitive } from "radix-ui";
|
||
import { Check, Minus } from "lucide-react";
|
||
|
||
import { cn } from "@/lib/utils";
|
||
|
||
/**
|
||
* v1.4.27 R3d MB4 / CF-40 — shadcn-style `<Checkbox>` primitive backed
|
||
* by `radix-ui`'s `Checkbox` namespace. Brought in so the Coach
|
||
* sources-rail can drop its raw `<input type="checkbox">` for a
|
||
* keyboard + touch accessible control that ships proper focus rings
|
||
* and a 44 px hit target on mobile.
|
||
*
|
||
* Mirrors the v1.4.27 shadcn `<Checkbox>` recipe: a Root that draws
|
||
* the border + checked-state background, an Indicator slot that
|
||
* shows the lucide `<Check>` when the box is checked.
|
||
*
|
||
* The Indicator also draws a `<Minus>` for the indeterminate state, which
|
||
* Radix's Root accepts natively via `checked="indeterminate"`. A group row
|
||
* whose leaves are partly selected is a real third state and needs to look
|
||
* like one — drawing it as unchecked would say "none of this is included",
|
||
* which on this surface is a false statement about what leaves the instance.
|
||
*/
|
||
function Checkbox({
|
||
className,
|
||
...props
|
||
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
||
return (
|
||
<CheckboxPrimitive.Root
|
||
data-slot="checkbox"
|
||
className={cn(
|
||
// The visual box stays 16×16, but the effective touch target is
|
||
// 32×32 via the switch's `::before` hit-area trick (v1.4.43 W5-H1):
|
||
// `relative` + an absolutely positioned 8 px halo that receives
|
||
// pointer events without disturbing layout. 16 px alone sits below
|
||
// the WCAG 2.5.8 24 px floor on the measurement/mood row selectors;
|
||
// 32 px clears it with margin while staying small enough not to
|
||
// blanket neighbouring controls in dense table rows.
|
||
"peer border-border/70 relative size-4 shrink-0 rounded border before:absolute before:inset-[-8px] before:content-['']",
|
||
"data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=checked]:text-background",
|
||
"data-[state=indeterminate]:bg-primary data-[state=indeterminate]:border-primary data-[state=indeterminate]:text-background",
|
||
"focus-visible:ring-ring/50 focus-visible:ring-2 focus-visible:outline-none",
|
||
"disabled:cursor-not-allowed disabled:opacity-50",
|
||
"transition-colors",
|
||
className,
|
||
)}
|
||
{...props}
|
||
>
|
||
<CheckboxPrimitive.Indicator
|
||
data-slot="checkbox-indicator"
|
||
className="flex items-center justify-center text-current"
|
||
>
|
||
{props.checked === "indeterminate" ? (
|
||
<Minus className="size-3" aria-hidden="true" />
|
||
) : (
|
||
<Check className="size-3" aria-hidden="true" />
|
||
)}
|
||
</CheckboxPrimitive.Indicator>
|
||
</CheckboxPrimitive.Root>
|
||
);
|
||
}
|
||
|
||
export { Checkbox };
|