fix(ui): introduce FieldGroup and unify the add-form rhythm

The measurement add form opened with a horizontal type row (label beside
the select) while every field below it stacked label-above-control, so
the form read with two competing rhythms. Introduce a small FieldGroup
primitive — the long-standing space-y-2 label-above-control idiom in one
place — and route the measurement, mood and baseline forms through it so
they share one vertical rhythm. The type row now stacks like the rest.

Output stays byte-identical for the already-vertical groups; only the
type row changes, by design.
This commit is contained in:
Marc-André Bombeck committed 2026-06-30 12:35:36 +02:00
1 parent ca067bfe3d
commit d0b4b1e4cd
4 files changed
+128 -75

No files matched your search

+51
View File
@@ -0,0 +1,51 @@
import type { ReactNode } from "react";
import { Label } from "@/components/ui/label";
import { cn } from "@/lib/utils";
/**
* Central form field-group primitive: one label-above-control rhythm so every
* add form reads with the same vertical spacing. Mirrors the long-standing
* `space-y-2` + `<Label htmlFor>` + control idiom byte-for-byte so adopting it
* is a no-op on rendered output.
*
* - `hint` renders below the control as muted fine print.
* - `labelAccessory` sits right-aligned in the label row (e.g. a char counter);
* when present the label row becomes a `justify-between` flex row.
*/
export function FieldGroup({
htmlFor,
label,
hint,
labelAccessory,
className,
labelClassName,
children,
}: {
htmlFor: string;
label: ReactNode;
hint?: ReactNode;
labelAccessory?: ReactNode;
className?: string;
labelClassName?: string;
children: ReactNode;
}) {
return (
<div className={cn("space-y-2", className)}>
{labelAccessory ? (
<div className="flex items-center justify-between gap-3">
<Label htmlFor={htmlFor} className={labelClassName}>
{label}
</Label>
{labelAccessory}
</div>
) : (
<Label htmlFor={htmlFor} className={labelClassName}>
{label}
</Label>
)}
{children}
{hint ? <p className="text-muted-foreground text-xs">{hint}</p> : null}
</div>
);
}