import { ReactNode, useEffect, useState } from "react"; import clsx from "~/utils/shared/ClassesUtils"; import { RadioGroup } from "~/ui/components/RadioGroup"; // Subframe migration (strangler shim): // Keeps the original InputRadioGroup API (options/value/setValue/...) but renders the Subframe // design-system `RadioGroup` (horizontal layout). The public props are unchanged. // // value/setValue keep the original `string | number | undefined` type. Subframe/Radix // RadioGroup speaks strings only, so values are stringified at the boundary and mapped back to // their original typed value (via the options lookup) before calling setValue. // // controlled + uncontrolled: Subframe's Root is internally controlled. The legacy field was // usually controlled via value/setValue, but setValue is optional, so the shim holds its own // state seeded from `value`. When the parent controls `value` it stays in sync; when it // doesn't, internal state keeps the selection from sticking. interface Props { name?: string; title?: string; options: { name: string | ReactNode; value: string | number | undefined; disabled?: boolean }[]; value?: string | number | undefined; setValue?: React.Dispatch>; className?: string; required?: boolean; disabled?: boolean; } const toKey = (value: string | number | undefined): string => (value === undefined ? "" : String(value)); export default function InputRadioGroup({ name, title, value, options, setValue, className, required, disabled }: Props) { const [actualValue, setActualValue] = useState(value); useEffect(() => { setActualValue(value); }, [value]); function onValueChange(nextKey: string) { // Map the stringified key back to the original typed option value. const matched = options.find((option) => toKey(option.value) === nextKey); const next = matched ? matched.value : nextKey; setActualValue(next); if (setValue) { setValue(next); } } return (
{options.map((option, idx) => ( ))}
); }