planmymeal/app/components/RadioGroup.tsx

52 lines
1.6 KiB
TypeScript
Raw Normal View History

2026-10-31 20:44:29 -05:00
"use client";
import type { ReactNode } from "react";
import { SegmentedControl, SegmentedControlItem } from "@/components/base/segmented-control/segmented-control";
import { cx } from "@/utils/cx";
2026-10-31 20:44:29 -05:00
export interface RadioOption<T extends string | number> {
value: T;
label: ReactNode;
/** Extra classes on the selected segment (e.g. a status tint). */
2026-10-31 20:44:29 -05:00
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>) {
2026-10-31 20:44:29 -05:00
return (
<SegmentedControl
2026-10-31 20:44:29 -05:00
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")}
2026-10-31 20:44:29 -05:00
>
{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>
2026-10-31 20:44:29 -05:00
);
}