import { forwardRef, type ReactNode } from 'react'; import { Text, View, type TextProps, type ViewProps } from 'react-native'; import { Check, CheckIndeterminateSmall } from '@cdx-ui/icons'; import { CheckboxRoot as CheckboxRootPrimitive, CheckboxGroup as CheckboxGroupPrimitive, Slot, dataAttributes, mergeDataAttributes, useCheckboxContext, type ICheckboxProps, } from '@cdx-ui/primitives'; import { cn } from '@cdx-ui/utils'; import { Icon, IconProps } from '../Icon'; import { checkboxGroupVariants, checkboxIconVariants, checkboxIndicatorVariants, checkboxLabelVariants, checkboxRootVariants, } from './styles'; // ============================================================================= // CHECKBOX ROOT // ============================================================================= export interface CheckboxProps extends ICheckboxProps { className?: string; children?: ReactNode; } const CheckboxRoot = forwardRef( ({ className, isIndeterminate, children, style, ...props }, ref) => { const computedClassName = cn(checkboxRootVariants(), className); return ( {children} ); }, ); CheckboxRoot.displayName = 'Checkbox'; // ============================================================================= // CHECKBOX INDICATOR (styled-only, reads context for data-* styling) // ============================================================================= /** Compound check × 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', 'indeterminate:false:false': 'indeterminate', 'indeterminate:true:false': 'indeterminate-invalid', 'indeterminate:false:true': 'indeterminate-active', 'indeterminate:true:true': 'indeterminate-invalid-active', } as const; export interface CheckboxIndicatorProps extends ViewProps { asChild?: boolean; className?: string; children?: ReactNode; } const CheckboxIndicator = forwardRef( ({ asChild, className, children, style, ...props }, ref) => { const { isChecked, isDisabled, isHovered, isInvalid, isReadOnly, isPressed, isFocused, isIndeterminate, isFocusVisible, } = useCheckboxContext(); const checkState = isIndeterminate ? 'indeterminate' : isChecked ? 'checked' : 'unchecked'; const fieldState = INDICATOR_STATES[ `${checkState}:${isInvalid ? 'true' : 'false'}:${isPressed ? 'true' : 'false'}` ]; const computedClassName = cn(checkboxIndicatorVariants(), className); const Comp = asChild ? Slot : View; return ( {children} ); }, ); CheckboxIndicator.displayName = 'Checkbox.Indicator'; // ============================================================================= // CHECKBOX ICON (styled-only, reads context for conditional mount) // ============================================================================= export interface CheckboxIconProps extends Omit {} const CheckboxIcon = ({ className, style, ...props }: CheckboxIconProps) => { const computedClassName = cn(checkboxIconVariants(), className); return ( ); }; CheckboxIcon.displayName = 'Checkbox.Icon'; // Internal helper that reads context for conditional rendering const CheckboxIconSlot = ({ isIndeterminate: isIndeterminateProp, }: { isIndeterminate?: boolean; }) => { const { isChecked, isIndeterminate } = useCheckboxContext(); const showIcon = isChecked || isIndeterminateProp || isIndeterminate; if (!showIcon) return null; return ( ); }; // ============================================================================= // CHECKBOX LABEL (styled-only) // ============================================================================= export interface CheckboxLabelProps extends TextProps { asChild?: boolean; className?: string; children?: ReactNode; } const CheckboxLabel = forwardRef( ({ asChild, className, children, style, ...props }, ref) => { const { isDisabled } = useCheckboxContext(); const computedClassName = cn(checkboxLabelVariants(), className); const Comp = asChild ? Slot : Text; return ( {children} ); }, ); CheckboxLabel.displayName = 'Checkbox.Label'; // ============================================================================= // CHECKBOX GROUP (wraps the primitive) // ============================================================================= export interface CheckboxGroupProps extends ViewProps { className?: string; children?: ReactNode; value?: string[]; onChange?: (value: string[]) => void; isDisabled?: boolean; isInvalid?: boolean; isReadOnly?: boolean; direction?: 'column' | 'row'; } const CheckboxGroup = forwardRef( ( { className, children, style, direction = 'column', value, onChange, isDisabled, isInvalid, isReadOnly, ...viewProps }, ref, ) => { const computedClassName = cn(checkboxGroupVariants({ direction }), className); return ( {children} ); }, ); CheckboxGroup.displayName = 'Checkbox.Group'; // ============================================================================= // COMPOUND COMPONENT // ============================================================================= type CheckboxCompoundComponent = typeof CheckboxRoot & { Indicator: typeof CheckboxIndicator; Icon: typeof CheckboxIcon; Label: typeof CheckboxLabel; Group: typeof CheckboxGroup; }; export const Checkbox = Object.assign(CheckboxRoot, { Indicator: CheckboxIndicator, Icon: CheckboxIcon, Label: CheckboxLabel, Group: CheckboxGroup, }) as CheckboxCompoundComponent;