import classNames from 'classnames'; import type { CheckboxRef } from 'rc-checkbox'; import RcCheckbox from 'rc-checkbox'; import * as React from 'react'; import warning from '../_util/warning'; import { ConfigContext } from '../config-provider'; import DisabledContext from '../config-provider/DisabledContext'; import { FormItemInputContext } from '../form/context'; import GroupContext from './GroupContext'; import useStyle from './style'; import Wave from '../_util/wave'; import { TARGET_CLS } from '../_util/wave/interface'; export interface AbstractCheckboxProps { prefixCls?: string; className?: string; rootClassName?: string; defaultChecked?: boolean; checked?: boolean; style?: React.CSSProperties; disabled?: boolean; title?: string; onChange?: (e: T) => void; onClick?: React.MouseEventHandler; onMouseEnter?: React.MouseEventHandler; onMouseLeave?: React.MouseEventHandler; onKeyPress?: React.KeyboardEventHandler; onKeyDown?: React.KeyboardEventHandler; value?: any; tabIndex?: number; name?: string; children?: React.ReactNode; id?: string; autoFocus?: boolean; type?: string; skipGroup?: boolean; } export interface CheckboxChangeEventTarget extends CheckboxProps { checked: boolean; } export interface CheckboxChangeEvent { target: CheckboxChangeEventTarget; stopPropagation: () => void; preventDefault: () => void; nativeEvent: MouseEvent; } export interface CheckboxProps extends AbstractCheckboxProps { indeterminate?: boolean; } const InternalCheckbox: React.ForwardRefRenderFunction = ( props, ref, ) => { const { prefixCls: customizePrefixCls, className, rootClassName, children, indeterminate = false, style, onMouseEnter, onMouseLeave, skipGroup = false, disabled, ...restProps } = props; const { getPrefixCls, direction, checkbox } = React.useContext(ConfigContext); const checkboxGroup = React.useContext(GroupContext); const { isFormItemInput } = React.useContext(FormItemInputContext); const contextDisabled = React.useContext(DisabledContext); const mergedDisabled = (checkboxGroup?.disabled || disabled) ?? contextDisabled; const prevValue = React.useRef(restProps.value); React.useEffect(() => { checkboxGroup?.registerValue(restProps.value); warning( 'checked' in restProps || !!checkboxGroup || !('value' in restProps), 'Checkbox', '`value` is not a valid prop, do you mean `checked`?', ); }, []); React.useEffect(() => { if (skipGroup) { return; } if (restProps.value !== prevValue.current) { checkboxGroup?.cancelValue(prevValue.current); checkboxGroup?.registerValue(restProps.value); prevValue.current = restProps.value; } return () => checkboxGroup?.cancelValue(restProps.value); }, [restProps.value]); const prefixCls = getPrefixCls('checkbox', customizePrefixCls); const [wrapSSR, hashId] = useStyle(prefixCls); const checkboxProps: CheckboxProps = { ...restProps }; if (checkboxGroup && !skipGroup) { checkboxProps.onChange = (...args) => { if (restProps.onChange) { restProps.onChange(...args); } if (checkboxGroup.toggleOption) { checkboxGroup.toggleOption({ label: children, value: restProps.value }); } }; checkboxProps.name = checkboxGroup.name; checkboxProps.checked = checkboxGroup.value.includes(restProps.value); } const classString = classNames( `${prefixCls}-wrapper`, { [`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-wrapper-checked`]: checkboxProps.checked, [`${prefixCls}-wrapper-disabled`]: mergedDisabled, [`${prefixCls}-wrapper-in-form-item`]: isFormItemInput, }, checkbox?.className, className, rootClassName, hashId, ); const checkboxClass = classNames( { [`${prefixCls}-indeterminate`]: indeterminate, }, TARGET_CLS, hashId, ); const ariaChecked = indeterminate ? 'mixed' : undefined; return wrapSSR( {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} , ); }; const Checkbox = React.forwardRef(InternalCheckbox); if (process.env.NODE_ENV !== 'production') { Checkbox.displayName = 'Checkbox'; } export default Checkbox;