import { forwardRef, type ReactNode } from 'react'; import { Text, type TextProps, View, type ViewProps } from 'react-native'; import { RadioRoot as RadioRootPrimitive, RadioGroup as RadioGroupPrimitive, Slot, dataAttributes, mergeDataAttributes, useRadioContext, type IRadioProps, } from '@cdx-ui/primitives'; import { cn } from '@cdx-ui/utils'; import { radioGroupVariants, radioIndicatorVariants, radioInnerDotVariants, radioLabelVariants, radioRootVariants, } from './styles'; // ============================================================================= // RADIO ROOT // ============================================================================= export interface RadioProps extends IRadioProps { className?: string; children?: ReactNode; } const RadioRoot = forwardRef(({ className, children, style, ...props }, ref) => { const computedClassName = cn(radioRootVariants(), className); return ( {children} ); }); RadioRoot.displayName = 'Radio'; // ============================================================================= // RADIO INDICATOR (styled-only, reads context for data-* styling) // ============================================================================= /** Compound checked × invalid × active state for native Uniwind selectors (single data-field-state). */ const INDICATOR_STATES = { 'unchecked:false:false': 'idle', 'unchecked:true:false': 'invalid', 'unchecked:false:true': 'active', 'unchecked:true:true': 'invalid-active', 'checked:false:false': 'checked', 'checked:true:false': 'checked-invalid', 'checked:false:true': 'checked-active', 'checked:true:true': 'checked-invalid-active', } as const; export interface RadioIndicatorProps extends ViewProps { asChild?: boolean; className?: string; children?: ReactNode; } const RadioIndicator = forwardRef( ({ asChild, className, children, style, ...props }, ref) => { const { isChecked, isDisabled, isHovered, isInvalid, isReadOnly, isPressed, isFocusVisible } = useRadioContext(); const checkState = isChecked ? 'checked' : 'unchecked'; const fieldState = INDICATOR_STATES[ `${checkState}:${isInvalid ? 'true' : 'false'}:${isPressed ? 'true' : 'false'}` ]; const computedClassName = cn(radioIndicatorVariants(), className); const Comp = asChild ? Slot : View; return ( {children} ); }, ); RadioIndicator.displayName = 'Radio.Indicator'; // ============================================================================= // RADIO LABEL (styled-only) // ============================================================================= export interface RadioLabelProps extends TextProps { asChild?: boolean; className?: string; children?: ReactNode; } const RadioLabel = forwardRef( ({ asChild, className, children, style, ...props }, ref) => { const computedClassName = cn(radioLabelVariants(), className); const Comp = asChild ? Slot : Text; return ( {children} ); }, ); RadioLabel.displayName = 'Radio.Label'; // ============================================================================= // RADIO GROUP (wraps the primitive) // ============================================================================= export interface RadioGroupProps extends ViewProps { className?: string; children?: ReactNode; value?: string; defaultValue?: string; onChange?: (value: string) => void; isDisabled?: boolean; isInvalid?: boolean; isRequired?: boolean; isReadOnly?: boolean; direction?: 'column' | 'row'; name?: string; 'aria-label'?: string; 'aria-labelledby'?: string; } const RadioGroup = forwardRef( ( { className, children, style, direction = 'column', value, defaultValue, onChange, isDisabled, isInvalid, isRequired, isReadOnly, name, ...viewProps }, ref, ) => { const computedClassName = cn(radioGroupVariants({ direction }), className); return ( {children} ); }, ); RadioGroup.displayName = 'Radio.Group'; // ============================================================================= // COMPOUND COMPONENT // ============================================================================= type RadioCompoundComponent = typeof RadioRoot & { Indicator: typeof RadioIndicator; Label: typeof RadioLabel; Group: typeof RadioGroup; }; export const Radio = Object.assign(RadioRoot, { Indicator: RadioIndicator, Label: RadioLabel, Group: RadioGroup, }) as RadioCompoundComponent;