import React, { useState, useEffect, forwardRef, useMemo } from 'react'; import { getWebProps } from 'react-native-unistyles/web'; import { CheckboxProps } from './types'; import { checkboxStyles } from './Checkbox.styles'; import { IconSvg } from '../Icon/IconSvg/IconSvg.web'; import useMergeRefs from '../hooks/useMergeRefs'; import { getWebSelectionAriaProps, generateAccessibilityId, combineIds } from '../utils/accessibility'; import type { IdealystElement } from '../utils/refTypes'; import { flattenStyle } from '../utils/flattenStyle'; /** * Checkbox input for boolean selection with support for indeterminate state. * Includes label, helper text, and error display options. */ const Checkbox = forwardRef(({ checked = false, indeterminate = false, disabled = false, onChange, size = 'md', intent = 'primary', variant = 'default', label, children, // Spacing variants from FormInputStyleProps margin, marginVertical, marginHorizontal, style, testID, required = false, error, helperText, id, // Accessibility props accessibilityLabel, accessibilityHint, accessibilityDisabled, accessibilityHidden, accessibilityRole, accessibilityLabelledBy, accessibilityDescribedBy, accessibilityControls, accessibilityExpanded, accessibilityPressed, accessibilityOwns, accessibilityHasPopup, accessibilityChecked, }, ref) => { const [internalChecked, setInternalChecked] = useState(checked); useEffect(() => { setInternalChecked(checked); }, [checked]); const handleChange = (event: React.ChangeEvent) => { event.preventDefault(); event.stopPropagation(); if (disabled) return; const newChecked = event.target.checked; setInternalChecked(newChecked); onChange?.(newChecked); }; // Generate unique IDs for accessibility const checkboxId = useMemo(() => id || generateAccessibilityId('checkbox'), [id]); const errorId = useMemo(() => `${checkboxId}-error`, [checkboxId]); const helperId = useMemo(() => `${checkboxId}-helper`, [checkboxId]); // Generate ARIA props for the input element const ariaProps = useMemo(() => { const labelContent = children || label; const computedLabel = accessibilityLabel ?? (typeof labelContent === 'string' ? labelContent : undefined); const computedChecked = accessibilityChecked ?? (indeterminate ? 'mixed' : internalChecked); const describedByIds = combineIds( accessibilityDescribedBy, error ? errorId : helperText ? helperId : undefined ); return getWebSelectionAriaProps({ accessibilityLabel: computedLabel, accessibilityHint, accessibilityDisabled: accessibilityDisabled ?? disabled, accessibilityHidden, accessibilityRole: accessibilityRole ?? 'checkbox', accessibilityLabelledBy, accessibilityDescribedBy: describedByIds, accessibilityControls, accessibilityExpanded, accessibilityPressed, accessibilityOwns, accessibilityHasPopup, accessibilityChecked: computedChecked, }); }, [ accessibilityLabel, children, label, accessibilityHint, accessibilityDisabled, disabled, accessibilityHidden, accessibilityRole, accessibilityLabelledBy, accessibilityDescribedBy, error, errorId, helperText, helperId, accessibilityControls, accessibilityExpanded, accessibilityPressed, accessibilityOwns, accessibilityHasPopup, accessibilityChecked, indeterminate, internalChecked, ]); // Apply variants checkboxStyles.useVariants({ size, intent, type: variant as any, checked: internalChecked, disabled, visible: internalChecked || indeterminate, error: Boolean(error), margin, marginVertical, marginHorizontal, }); // Create style arrays - call as functions with required props for theme reactivity const wrapperStyleArray = [(checkboxStyles.wrapper as any)({}), flattenStyle(style)]; const containerStyleArray = [(checkboxStyles.container as any)({})]; const checkboxStyleArray = [(checkboxStyles.checkbox as any)({ intent, checked: internalChecked, disabled, type: variant })]; const labelStyleArray = [(checkboxStyles.label as any)({ disabled })]; const helperTextStyleArray = [(checkboxStyles.helperText as any)({ error: !!error })]; const checkmarkStyleArray = [(checkboxStyles.checkmark as any)({ checked: internalChecked })]; // Generate web props const wrapperProps = getWebProps(wrapperStyleArray); const containerProps = getWebProps(containerStyleArray); const checkboxProps = getWebProps(checkboxStyleArray); const labelProps = getWebProps(labelStyleArray); const helperTextProps = getWebProps(helperTextStyleArray); const checkmarkProps = getWebProps(checkmarkStyleArray); const labelContent = children || label; const displayHelperText = error || helperText; const mergedRef = useMergeRefs(ref, wrapperProps.ref); return (
{displayHelperText && (
{error || helperText}
)}
); }); Checkbox.displayName = 'Checkbox'; export default Checkbox;