UNPKG

@chayns-components/core

Version:

A set of beautiful React components for developing your own applications with chayns.

169 lines 6.83 kB
import { AnimatePresence } from 'motion/react'; import React, { useCallback, useContext, useEffect, useMemo, useState, useRef } from 'react'; import { RadioButtonGroupContext } from './radio-button-group/RadioButtonGroup'; import { StyledLabelWrapper, StyledMotionRadioButtonChildren, StyledRadioButton, StyledRadioButtonCheckBox, StyledRadioButtonCheckBoxMark, StyledRadioButtonLabel, StyledRadioButtonPseudoCheckBox, StyledRadioButtonWrapper } from './RadioButton.styles'; import { getHeightOfSingleTextLine } from '../../utils/calculate'; import { useKeyboardFocusHighlighting } from '../../hooks/useKeyboardFocusHighlighting'; const RadioButton = ({ children, shouldShowRightElementOnlyOnChecked = false, label, id, rightElement, shouldShowCentered = true, isDisabled = false, shouldEnableKeyboardHighlighting }) => { const { selectedRadioButtonId, updateSelectedRadioButtonId, radioButtonRightElements, updateHasRightElement, radioButtonsCanBeUnchecked, shouldEnableKeyboardHighlighting: shouldEnableKeyboardHighlightingFromGroup } = useContext(RadioButtonGroupContext); const [internalIsChecked, setInternalIsChecked] = useState(false); const [isHovered, setIsHovered] = useState(false); const [radioButtonTop, setRadioButtonTop] = useState(undefined); const radioButtonBoxRef = useRef(null); const radioButtonRootRef = useRef(null); const isInGroup = typeof updateSelectedRadioButtonId === 'function'; const isMarked = isInGroup ? selectedRadioButtonId === id : internalIsChecked; const uncheckable = radioButtonsCanBeUnchecked; const effectiveShouldEnableKeyboardHighlighting = shouldEnableKeyboardHighlightingFromGroup ?? shouldEnableKeyboardHighlighting; const shouldShowKeyboardHighlighting = useKeyboardFocusHighlighting(effectiveShouldEnableKeyboardHighlighting && !isDisabled); useEffect(() => { if (radioButtonRootRef.current && !shouldShowCentered) { const singleLineHeight = getHeightOfSingleTextLine({ container: radioButtonRootRef.current }); const boxHeight = radioButtonBoxRef.current?.getBoundingClientRect().height ?? 0; setRadioButtonTop((singleLineHeight - boxHeight) / 2); } }, [shouldShowCentered]); const handleClick = useCallback(() => { if (isDisabled) { return; } if (uncheckable) { if (updateSelectedRadioButtonId) { updateSelectedRadioButtonId(id === selectedRadioButtonId ? undefined : id); } setInternalIsChecked(prev => !prev); return; } if (typeof updateSelectedRadioButtonId === 'function') { updateSelectedRadioButtonId(id); } setInternalIsChecked(true); }, [id, isDisabled, uncheckable, selectedRadioButtonId, updateSelectedRadioButtonId]); const handleMouseEnter = useCallback(() => { if (!isDisabled) { setIsHovered(true); } }, [isDisabled]); const handleMouseLeave = () => { setIsHovered(false); }; const handleKeyDown = useCallback(event => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); handleClick(); } }, [handleClick]); const radioButtonRightElementMargin = useMemo(() => { if (!radioButtonRightElements) { return 'NONE'; } const index = radioButtonRightElements.findIndex(element => element.id === id); if (index < 0) { return 'NONE'; } const prevButton = radioButtonRightElements[index - 1]; const currentButton = radioButtonRightElements[index]; const nextButton = radioButtonRightElements[index + 1]; if (!currentButton?.hasRightElement) { return 'NONE'; } switch (true) { case prevButton?.hasRightElement && !nextButton?.hasRightElement: return 'TOP'; case !prevButton?.hasRightElement && nextButton?.hasRightElement: return 'BOTTOM'; case currentButton?.hasRightElement && !nextButton?.hasRightElement && !prevButton?.hasRightElement: return 'NONE'; default: return 'BOTH'; } }, [id, radioButtonRightElements]); const shouldShowRightElement = useMemo(() => { if (rightElement) { if (shouldShowRightElementOnlyOnChecked) { return isMarked; } return true; } return false; }, [isMarked, rightElement, shouldShowRightElementOnlyOnChecked]); useEffect(() => { if (typeof updateHasRightElement === 'function') { window.setTimeout(() => { updateHasRightElement(id, shouldShowRightElement); }, 10); } }, [id, shouldShowRightElement, updateHasRightElement]); return useMemo(() => /*#__PURE__*/React.createElement(StyledRadioButton, { $isDisabled: isDisabled, $radioButtonRightElementMargin: radioButtonRightElementMargin }, /*#__PURE__*/React.createElement(StyledRadioButtonWrapper, { ref: radioButtonRootRef, $shouldShowKeyboardHighlighting: shouldShowKeyboardHighlighting }, /*#__PURE__*/React.createElement(StyledRadioButtonCheckBox, { onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onKeyDown: handleKeyDown, disabled: isDisabled, $isDisabled: isDisabled, $shouldShowKeyboardHighlighting: shouldShowKeyboardHighlighting, type: "radio", checked: isMarked, onChange: () => {} }), /*#__PURE__*/React.createElement(StyledRadioButtonPseudoCheckBox, { $isDisabled: isDisabled, $isChecked: isMarked, ref: radioButtonBoxRef, onClick: handleClick, style: { top: shouldShowCentered ? '50%' : radioButtonTop, transform: shouldShowCentered ? 'translateY(-50%)' : undefined } }, /*#__PURE__*/React.createElement(StyledRadioButtonCheckBoxMark, { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, $isHovered: isHovered, $isSelected: isMarked, $isDisabled: isDisabled })), /*#__PURE__*/React.createElement(StyledLabelWrapper, null, label && /*#__PURE__*/React.createElement(StyledRadioButtonLabel, { $isDisabled: isDisabled, onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave }, label), shouldShowRightElement && rightElement)), children && /*#__PURE__*/React.createElement(AnimatePresence, { initial: false }, /*#__PURE__*/React.createElement(StyledMotionRadioButtonChildren, { animate: isMarked ? { opacity: 1, height: 'auto' } : { opacity: 0, height: 0 }, transition: { duration: 0.2 } }, children))), [children, handleClick, handleKeyDown, handleMouseEnter, isDisabled, isHovered, isMarked, label, radioButtonRightElementMargin, radioButtonTop, rightElement, shouldShowCentered, shouldShowKeyboardHighlighting, shouldShowRightElement]); }; RadioButton.displayName = 'RadioButton'; export default RadioButton; //# sourceMappingURL=RadioButton.js.map