UNPKG

@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
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;