@syncfusion/react-buttons
Version:
Syncfusion React Buttons package is a feature-rich collection of UI components including Button, CheckBox, RadioButton, Switch, Chip, and more for building modern, interactive React applications.
142 lines (141 loc) • 7.14 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect, useCallback, useImperativeHandle, useRef, forwardRef, useMemo } from 'react';
import { preRender, useProviderContext, useRippleEffect, Color, Size, Position, useStableId } from '@syncfusion/react-base';
import { CheckLargeIcon, IntermediateBarIcon } from '@syncfusion/react-icons';
const CHECK = 'sf-checkbox-checked';
const DISABLED = 'sf-disabled sf-no-pointer';
const FRAME = 'sf-checkbox-frame';
const INDETERMINATE = 'sf-checkbox-indeterminate';
const LABEL = 'sf-label';
const WRAPPER = 'sf-control sf-checkbox-wrapper';
const CHECKBOX_CLASS = 'sf-checkbox';
/**
* The Checkbox component allows users to select one or multiple options from a list, providing a visual representation of a binary choice with states like checked, unchecked, or indeterminate.
*
* ```typescript
* import { Checkbox } from "@syncfusion/react-buttons";
*
* <Checkbox checked={true} label="Accept Terms and Conditions" />
* ```
*/
export const Checkbox = forwardRef((props, ref) => {
const { onChange, checked, defaultChecked = false, color = Color.Primary, icon, checkedIcon, indeterminateIcon, className = '', disabled = false, indeterminate = false, labelPlacement = Position.Right, name = '', label = '', value = '', size = Size.Medium, id, required, valid, validationMessage = '', validityStyles = true, ...domProps } = props;
const isControlled = checked !== undefined;
const [checkedState, setCheckedState] = useState(() => {
if (isControlled) {
return !!checked;
}
return defaultChecked;
});
const [isIndeterminate, setIsIndeterminate] = useState(indeterminate);
const [isFocused, setIsFocused] = useState(false);
const inputRef = useRef(null);
const wrapperRef = useRef(null);
const rippleContainerRef = useRef(null);
const { dir, ripple } = useProviderContext();
const { rippleMouseDown, Ripple } = useRippleEffect(ripple, { isCenterRipple: true });
const isReadOnly = !!domProps.readOnly && !disabled;
const ariaReadonlyValue = isReadOnly ? 'true' : undefined;
const requiredAttribute = (!disabled && (required === true || valid === false)) ? true : undefined;
const isChecked = isControlled ? !!checked : checkedState;
const isInputValid = valid !== undefined ? !!valid : (required ? isChecked : true);
const ariaInvalidValue = !isInputValid ? 'true' : undefined;
const uniqueId = id ?? useStableId('sf-checkbox');
const publicAPI = useMemo(() => ({
checked,
indeterminate,
value,
color,
size,
icon,
checkedIcon
}), [checked, indeterminate, value, color, size, icon, checkedIcon]);
useImperativeHandle(ref, () => ({
...publicAPI,
element: inputRef.current
}), [publicAPI]);
useEffect(() => {
const element = inputRef.current;
if (!element || typeof element.setCustomValidity !== 'function') {
return;
}
if (isInputValid) {
element.setCustomValidity('');
}
else if (validationMessage) {
element.setCustomValidity(validationMessage);
}
}, [isInputValid, validationMessage]);
useEffect(() => {
if (isControlled) {
setCheckedState(!!checked);
}
}, [checked, isControlled]);
useEffect(() => {
preRender('checkbox');
}, []);
useEffect(() => {
setIsIndeterminate(indeterminate);
}, [indeterminate]);
const handleStateChange = useCallback((event) => {
if (isReadOnly) {
return;
}
const newChecked = event.target.checked;
if (!isControlled) {
setCheckedState(newChecked);
}
if (onChange) {
onChange({ event, value: newChecked });
}
}, [onChange, isControlled, isReadOnly]);
const handleFocus = () => {
setIsFocused(true);
};
const handleBlur = () => {
setIsFocused(false);
};
const handleMouseDown = useCallback((e) => {
if (!ripple || !rippleContainerRef.current || !rippleMouseDown) {
return;
}
const syntheticEvent = {
...e,
currentTarget: rippleContainerRef.current,
target: rippleContainerRef.current,
preventDefault: e.preventDefault,
stopPropagation: e.stopPropagation
};
rippleMouseDown(syntheticEvent);
}, [ripple, rippleMouseDown]);
const handleKeyDown = useCallback((e) => {
if (isReadOnly && (e.key === ' ' || e.key === 'Spacebar' || e.key === 'Enter')) {
e.preventDefault();
e.stopPropagation();
}
}, [isReadOnly]);
const wrapperClass = useMemo(() => [
WRAPPER,
className,
color && color.toLowerCase() !== 'secondary' ? `sf-${color.toLowerCase()}` : '',
disabled ? DISABLED : '',
isFocused ? 'sf-focus' : '',
dir === 'rtl' ? 'sf-rtl' : '',
size && size.toLowerCase() !== 'medium' ? `sf-${size.toLowerCase()}` : '',
isReadOnly ? 'sf-readonly' : ''
].filter(Boolean).join(' '), [className, color, disabled, isFocused, dir, size, isReadOnly]);
const renderRipple = () => (_jsxs("span", { ref: rippleContainerRef, className: `sf-checkbox-ripple sf-checkbox-ripple-${size.toLowerCase().substring(0, 2)}`, children: [ripple && _jsx(Ripple, {}), _jsx("input", { ref: inputRef, id: uniqueId, className: `${CHECKBOX_CLASS} ${className}`, type: "checkbox", name: name, value: value, checked: isChecked, disabled: disabled, "aria-readonly": ariaReadonlyValue, required: requiredAttribute, "aria-invalid": ariaInvalidValue, onFocus: handleFocus, onBlur: handleBlur, onChange: handleStateChange, onKeyDown: handleKeyDown, ...domProps }), isChecked && checkedIcon ? checkedIcon : !checkedState && icon ? icon : renderIcons()] }));
const showError = validityStyles !== false && !isInputValid;
const renderIcons = () => {
const iconError = showError && !isChecked && !isIndeterminate;
return (_jsxs("span", { className: [
'sf-checkbox-icons',
`${FRAME}-${size.toLowerCase().substring(0, 2)}`,
isIndeterminate ? INDETERMINATE : isChecked ? CHECK : '',
iconError ? 'sf-error' : ''
].filter(Boolean).join(' '), children: [isIndeterminate && indeterminateIcon ? (indeterminateIcon) : isIndeterminate && (_jsx(IntermediateBarIcon, { width: 20, height: 20, fill: 'currentColor' })), isChecked && !isIndeterminate && (_jsx(CheckLargeIcon, { fill: "currentColor" }))] }));
};
return (_jsx("div", { ref: wrapperRef, className: wrapperClass, "aria-disabled": disabled ? 'true' : 'false', onMouseDown: handleMouseDown, children: _jsxs("label", { className: `sf-checkbox-label sf-${labelPlacement.toLowerCase()} sf-checkbox-${size.toLowerCase()}`, children: [label && (_jsx("span", { className: `${LABEL}`, children: label })), renderRipple()] }) }));
});
Checkbox.displayName = 'Checkbox';
export default Checkbox;