import React, { createContext, forwardRef, useCallback, useContext, useState } from 'react'; interface RadioGroupContextValue { name?: string; value?: string; onChange?: React.ChangeEventHandler; } const RadioGroupContext = createContext(null); export interface RadioProps extends Omit< React.InputHTMLAttributes, 'type' > { label?: React.ReactNode; labelClassName?: string; } export const Radio = forwardRef( ( { label, labelClassName = '', className = '', id, name, value, checked, onChange, ...rest }, ref ) => { const group = useContext(RadioGroupContext); const resolvedName = name ?? group?.name; const resolvedChecked = checked !== undefined ? checked : group && value !== undefined ? group.value === value : undefined; const resolvedOnChange = onChange ?? group?.onChange; const input = ( ); if (label === undefined) return input; const classes = ['radio', labelClassName].filter(Boolean).join(' '); return ( ); } ); Radio.displayName = 'Radio'; export interface RadioGroupProps extends Omit< React.HTMLAttributes, 'onChange' | 'defaultValue' > { name: string; /** Controlled selected value. When set the group ignores `defaultValue`. */ value?: string; /** Uncontrolled initial value. Used when `value` is omitted. */ defaultValue?: string; onChange?: React.ChangeEventHandler; } export const RadioGroup = forwardRef( ( { name, value, defaultValue, onChange, className = '', children, ...rest }, ref ) => { const isControlled = value !== undefined; const [internalValue, setInternalValue] = useState( defaultValue ); const resolvedValue = isControlled ? value : internalValue; const handleChange = useCallback< React.ChangeEventHandler >( event => { if (!isControlled) { setInternalValue(event.target.value); } onChange?.(event); }, [isControlled, onChange] ); const classes = ['radio-group', className].filter(Boolean).join(' '); return (
{children}
); } ); RadioGroup.displayName = 'RadioGroup';