planmymeal/app/components/RadioGroup.tsx
Cayden Schwechten df5dd3a70e Move UI to BoardUI with slate + salmon theme; add PDF plan export
- Adopt BoardUI (Pro init): theme tokens, Inter, .dark mode, Button,
  SegmentedControl, Meter, Skeleton and ThemeToggle; remap the app's
  color aliases onto BoardUI tokens and swap lucide for Remix icons
- Retint BoardUI's neutral ramp to cool slate with a salmon accent;
  drop the dotted background; raise muted text contrast
- Simplify the landing page and remove the overlap explorer
- Planner: explicit "I have this" toggle on grocery lines, pantry panel
  above the menu with the staples list spelled out, sticky aisle
  header fix, and a "Download PDF" plan export (jsPDF, lazy-loaded)
- Recipes: replace the family cabbage soup with Cade's Cabbage Soup
  (adds Bone-In Ham Steak to the catalog); remove Ham, Bean & Cabbage Soup
- Remove unused BoardUI components, dependencies and exports

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:36:33 -06:00

51 lines
1.6 KiB
TypeScript

"use client";
import type { ReactNode } from "react";
import { SegmentedControl, SegmentedControlItem } from "@/components/base/segmented-control/segmented-control";
import { cx } from "@/utils/cx";
export interface RadioOption<T extends string | number> {
value: T;
label: ReactNode;
/** Extra classes on the selected segment (e.g. a status tint). */
activeClass?: string;
}
interface RadioGroupProps<T extends string | number> {
name: string;
options: readonly RadioOption<T>[];
value: T;
onChange: (value: T) => void;
size?: "xs" | "sm";
fullWidth?: boolean;
}
/** Single-select segmented control: BoardUI's SegmentedControl (React Aria handles roving focus + arrow keys). */
export function RadioGroup<T extends string | number>({ name, options, value, onChange, size = "sm", fullWidth = false }: RadioGroupProps<T>) {
return (
<SegmentedControl
aria-label={name}
selectedKeys={[value]}
onSelectionChange={(keys) => {
const next = options.find((o) => keys.has(o.value));
if (next) onChange(next.value);
}}
className={cx("max-w-full", fullWidth ? "flex w-full" : "shrink-0")}
>
{options.map((option) => (
<SegmentedControlItem
key={String(option.value)}
id={option.value}
className={cx(
"tabular-nums",
size === "xs" && "min-h-7 px-2 text-caption-1-medium",
fullWidth && "min-w-0 flex-1",
option.value === value && option.activeClass,
)}
>
{option.label}
</SegmentedControlItem>
))}
</SegmentedControl>
);
}