import { forwardRef, ReactElement, ReactNode, useEffect, useImperativeHandle, useRef } from 'react'; import classnames from 'classnames'; import styles from './InternalCheckbox.css'; import Box from '../Box'; import focusStyles from '../Focus.css'; import Icon from '../Icon'; import Label from '../Label'; import FormErrorMessage from '../sharedSubcomponents/FormErrorMessage'; import FormHelperText from '../sharedSubcomponents/FormHelperText'; import Text from '../Text'; import useFocusVisible from '../useFocusVisible'; import useInteractiveStates from '../utils/useInteractiveStates'; import useTapScaleAnimation from '../utils/useTapScaleAnimation'; type Props = { checked?: boolean; disabled?: boolean; errorMessage?: string; helperText?: string; id: string; image?: ReactNode; indeterminate?: boolean; label?: string; labelDisplay?: 'visible' | 'hidden'; name?: string; onChange?: (arg1: { event: React.ChangeEvent; checked: boolean }) => void; onClick?: (arg1: { event: React.ChangeEvent; checked: boolean }) => void; /** * Make the checkbox readonly. Interactivity is disabled, but it can be used as a visual indicator. Click handler events are also disabled */ readOnly?: boolean; ref?: ReactElement; // eslint-disable-line react/no-unused-prop-types, size?: 'sm' | 'md'; style?: { borderColor?: string; backgroundColor?: string; }; }; const InternalCheckboxWithForwardRef = forwardRef(function Checkbox( { checked = false, disabled = false, errorMessage, helperText, id, image, indeterminate = false, label, labelDisplay = 'visible', name, onChange, onClick, readOnly = false, size = 'md', style, }: Props, ref, ) { const innerRef = useRef(null); // When using both forwardRef and innerRef, React.useimperativehandle() allows a parent component // that renders to call inputRef.current.focus() // @ts-expect-error - TS2322 - Type 'HTMLInputElement | null' is not assignable to type 'HTMLInputElement'. useImperativeHandle(ref, () => innerRef.current); useEffect(() => { if (innerRef && innerRef.current) { innerRef.current.indeterminate = indeterminate; } }, [indeterminate]); const handleChange = (event: React.ChangeEvent) => { if (onChange) { onChange({ event, checked: event.target.checked }); } }; const handleClick = (event: React.ChangeEvent) => { if (onClick) { onClick({ event, checked: event.currentTarget.checked }); } }; const { handleOnMouseEnter, handleOnMouseLeave, handleOnBlur, handleOnFocus, handleOnMouseDown, handleOnMouseUp, isFocused, isHovered, isActive, } = useInteractiveStates(); const { isFocusVisible } = useFocusVisible(); const tapScaleAnimation = useTapScaleAnimation(); let ariaDescribedby; if (errorMessage) { ariaDescribedby = `${id}-error`; } if (label && helperText) { ariaDescribedby = `${id}-helperText`; } const iconSizes = { sm: 8, md: 12, }; const borderRadiusStyle = size === 'sm' ? styles.borderRadiusSm : styles.borderRadiusMd; const styleSize = size === 'sm' ? styles.sizeSm : styles.sizeMd; const textColor = disabled ? 'subtle' : undefined; const unchecked = !(checked || indeterminate); const bgStyle = classnames({ [styles.enabled]: !disabled && unchecked, [styles.disabled]: disabled, [styles.checked]: !unchecked && !disabled, }); const borderStyle = classnames({ [styles.border]: !disabled && unchecked && !errorMessage && !isHovered, [styles.borderDisabled]: disabled, [styles.borderSelected]: !disabled && !unchecked, [styles.borderErrorUnchecked]: errorMessage && unchecked, [styles.borderHovered]: !disabled && isHovered && !isActive && unchecked && !errorMessage, }); const divStyles = classnames( bgStyle, borderStyle, borderRadiusStyle, styleSize, styles.check, styles.thickBorder, { [focusStyles.accessibilityOutlineFocus]: isFocused && isFocusVisible, }, ); const inputStyles = classnames(styles.input, styleSize, { [styles.inputEnabled]: !disabled, [styles.readOnly]: readOnly, }); return (
{(checked || indeterminate) && ( )}
) => void' is not assignable to type 'MouseEventHandler'. onClick={handleClick} onFocus={handleOnFocus} onMouseDown={() => { handleOnMouseDown(); }} onMouseEnter={handleOnMouseEnter} onMouseLeave={handleOnMouseLeave} onMouseUp={() => { handleOnMouseUp(); }} type="checkbox" />
{Boolean(image) && {image}} {label && ( {helperText ? ( ) : null} {errorMessage ? ( ) : null} )}
); }); InternalCheckboxWithForwardRef.displayName = 'InternalCheckbox'; export default InternalCheckboxWithForwardRef;